From 5fa327001a3620c52273ebe9ce2da0f387141638 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Mon, 10 Nov 2025 16:32:31 +0100 Subject: [PATCH 01/18] create the parser. No error handling --- src/controllers/OredicController.ts | 18 ++ src/routes/leveret/leveret.ts | 7 + src/routes/leveret/oredic/oredic-ae2.ts | 26 +++ src/utils/parsers/Oredic.ts | 298 ++++++++++++++++++++++++ 4 files changed, 349 insertions(+) create mode 100644 src/controllers/OredicController.ts create mode 100644 src/routes/leveret/oredic/oredic-ae2.ts create mode 100644 src/utils/parsers/Oredic.ts diff --git a/src/controllers/OredicController.ts b/src/controllers/OredicController.ts new file mode 100644 index 0000000..9d0dd03 --- /dev/null +++ b/src/controllers/OredicController.ts @@ -0,0 +1,18 @@ +import { RequestHandler, Request, Response, NextFunction } from "express"; +import { Ae2uelOredicParser } from "../utils/parsers/Oredic"; +import { getLogger } from "../utils/Logger"; + +export class OredicController { + constructor() {} + + async answer(req: Request) { + const Parser = new Ae2uelOredicParser(req.body.string); + return Parser.parse(); + } + + handler = + (): RequestHandler => + async (req: Request, res: Response, next: NextFunction) => { + res.json({ ast: await this.answer(req) }); + }; +} diff --git a/src/routes/leveret/leveret.ts b/src/routes/leveret/leveret.ts index 7ebf16b..f2ef874 100644 --- a/src/routes/leveret/leveret.ts +++ b/src/routes/leveret/leveret.ts @@ -1,6 +1,7 @@ import { Router } from "express"; import { grokRoutes } from "./grok/grok"; import { setEndpointData } from "../../middleware/setEndpointData"; +import { oredicRoutes } from "./oredic/oredic-ae2"; export function leveretRoutes() { const router = Router(); @@ -13,5 +14,11 @@ export function leveretRoutes() { grokRoutes() ); + router.use( + "/oredic-ae2", + setEndpointData("main", "oredic-ae2"), + oredicRoutes() + ); + return router; } diff --git a/src/routes/leveret/oredic/oredic-ae2.ts b/src/routes/leveret/oredic/oredic-ae2.ts new file mode 100644 index 0000000..36c93be --- /dev/null +++ b/src/routes/leveret/oredic/oredic-ae2.ts @@ -0,0 +1,26 @@ +import { Router } from "express"; +import { GrokController } from "../../../controllers/GrokController"; +import { cleanup } from "../../../middleware/cleanup"; +import { endpointData } from "../../../middleware/endpointData"; +import { filterBody } from "../../../middleware/filterBody"; +import { globalRateLimits } from "../../../middleware/globalRateLimits"; +import { setEndpointData } from "../../../middleware/setEndpointData"; +import { tokenCheckerGrok } from "../../../middleware/tokenChecker"; +import { userRateLimits } from "../../../middleware/userRateLimits"; +import { zodValidator } from "../../../middleware/validator"; +import { GrokInputDataSchema } from "../../../types/grok"; +import { OredicController } from "../../../controllers/OredicController"; + +export function oredicRoutes() { + const router = Router(); + const controller = new OredicController(); + + router.post( + "/nomi-ceu", + setEndpointData("child", "nomi-ceu"), + endpointData, + controller.handler() + ); + + return router; +} diff --git a/src/utils/parsers/Oredic.ts b/src/utils/parsers/Oredic.ts new file mode 100644 index 0000000..1a7b057 --- /dev/null +++ b/src/utils/parsers/Oredic.ts @@ -0,0 +1,298 @@ +/* + * Meant to imitate the logic in + * https://github.com/AE2-UEL/Applied-Energistics-2/blob/26bb5986c636e9bdde62559b0d1c2bbc48c4b9e3/src/main/java/appeng/util/item/OreDictFilterMatcher.java#L11 + */ + +import { getLogger } from "../Logger"; + +type Ast = AstNode | null; + +type AstNode = PatternNode | OperatorNode; +type PatternNode = { + type: "pattern"; + negation: boolean; + children: Array; +}; +type OperatorNode = { + type: "operator"; + operator: "AND" | "OR" | "XOR"; + negation: boolean; + children: Array; +}; + +type Token = { type: "text"; content: string } | { type: "wildcard" }; + +type LexemeElement = { + type: "group" | "operator" | "negation" | "wildcard" | "text"; + content: LexemeElement[] | string | null; +}; + +export class Ae2uelOredicParser { + lexemeBuffer: string; + + parenthesesCount: number; + + pos: 0; + + negationFlag: boolean; + currentOperator: "AND" | "XOR" | "OR" | null; + + constructor(private oredicString: string) { + this.lexemeBuffer = ""; + + this.parenthesesCount = 0; + + this.pos = 0; + + this.negationFlag = false; + this.currentOperator = null; + } + + parse() { + getLogger().simpleLog("debug", `parsing: ${this.oredicString}`); + + const lexemeList = this.lexicalParse(this.oredicString.split("")); + if (this.parenthesesCount !== 0) { + // Handle Error. Parantheses should be balanced + } + + const ast = this.parseNode(lexemeList); + getLogger().simpleLog( + "debug", + `Ast Parse: ${JSON.stringify(ast, undefined, 4)}` + ); + return ast; + } + + /* + * This function iterates over the input once and makes an ordered list of lexeme + * elements. This groups every kind of node together in only one element, making the + * further parsing easier. + */ + private lexicalParse(pattern: string[]) { + let lexemeList: LexemeElement[] = []; + + for (let i = 0; i < pattern.length; i++) { + switch (pattern[i]) { + case " ": + break; + + case "(": + lexemeList = this.flushLexemeBuffer(lexemeList); + this.updateParCount("START"); + + const subList = this.lexicalParse(pattern.slice(i + 1)); + lexemeList.push({ type: "group", content: subList }); + + i = this.pos + 1; + + break; + case ")": + this.flushLexemeBuffer(lexemeList); + this.updateParCount("END"); + return lexemeList; + + case "&": + lexemeList = this.flushLexemeBuffer(lexemeList); + lexemeList.push({ type: "operator", content: "AND" }); + break; + case "^": + lexemeList = this.flushLexemeBuffer(lexemeList); + lexemeList.push({ type: "operator", content: "XOR" }); + break; + case "|": + lexemeList = this.flushLexemeBuffer(lexemeList); + lexemeList.push({ type: "operator", content: "OR" }); + break; + + case "!": + lexemeList = this.flushLexemeBuffer(lexemeList); + lexemeList.push({ type: "negation", content: null }); + break; + + case "*": + lexemeList = this.flushLexemeBuffer(lexemeList); + lexemeList.push({ type: "wildcard", content: null }); + break; + + default: + this.lexemeBuffer += pattern[i]; + break; + } + this.pos += 1; + } + + this.flushLexemeBuffer(lexemeList); + return lexemeList; + } + + /* + * This is the function that actually parses the Lexeme List for the logic. + */ + private parseNode(lexemeList: LexemeElement[]): Ast { + let ast: Ast = null; + let tokenBuffer: Token[] = []; + let operandBuffer: Array = []; + + for (let i = 0; i < lexemeList.length; i++) { + switch (lexemeList[i].type) { + case "group": + const subLexeme = lexemeList[i].content; + if (typeof subLexeme === "string" || !subLexeme) { + // Handle Error. Isn't an array. + return null; + } + const subAst: Ast = this.parseNode(subLexeme); + + if (!subAst) { + // Handle Error. Empty sub Ast + return null; + } + + if (!ast) { + ast = subAst; + } else { + operandBuffer.push(subAst); + } + + break; + + case "operator": + const operator = lexemeList[i].content; + if (operator !== "AND" && operator !== "OR" && operator !== "XOR") { + // Handle Error. No valid operator + return null; + } + + if (tokenBuffer.length > 0) { + operandBuffer.push( + this.createPatternNode(tokenBuffer, this.negationFlag) + ); + tokenBuffer = []; + this.negationFlag = false; + } + + if (operandBuffer.length > 0) { + ast = this.flushOperatorNode(ast, operator, operandBuffer); + operandBuffer = []; + } + + this.currentOperator = operator; + + break; + + case "negation": + if (tokenBuffer.length !== 0) { + // Handle Error. Negation in the middle of a text element + return null; + } + + this.negationFlag = !this.negationFlag; + break; + + case "wildcard": + tokenBuffer.push({ type: "wildcard" }); + break; + + case "text": + const content = lexemeList[i].content; + if (typeof content !== "string") { + // Handle Error. Text Lexeme with no Content + return null; + } + tokenBuffer.push({ type: "text", content: content }); + break; + } + } + ast = this.flushEnd(ast, tokenBuffer, operandBuffer); + return ast; + } + + private flushLexemeBuffer(lexemeList: LexemeElement[]) { + if (this.lexemeBuffer === "") { + return lexemeList; + } + lexemeList.push({ type: "text", content: this.lexemeBuffer }); + this.lexemeBuffer = ""; + + return lexemeList; + } + + private flushOperatorNode( + ast: Ast, + situation: "AND" | "XOR" | "OR", + content: AstNode[] + ): Ast { + if (!content) { + // Handle error. No content + return null; + } + + if (!ast) { + return content[0]; + } + + if (ast.type === "pattern" || this.currentOperator !== situation) { + return { + type: "operator", + operator: situation, + negation: false, + children: [ast].concat(content), + }; + } + + if (this.currentOperator === situation) { + ast.children = ast.children.concat(content); + return ast; + } + + return ast; + } + + private flushEnd( + ast: Ast, + tokenBuffer: Token[], + operandBuffer: AstNode[] + ): Ast { + if (tokenBuffer.length > 0) { + operandBuffer.push( + this.createPatternNode(tokenBuffer, this.negationFlag) + ); + } + + if (operandBuffer.length > 0 && this.currentOperator) { + ast = this.flushOperatorNode(ast, this.currentOperator, operandBuffer); + } + + return ast; + } + + private updateParCount(side: "START" | "END") { + switch (side) { + case "START": + this.parenthesesCount += 1; + break; + + case "END": + if (this.parenthesesCount === 0) { + // Handle Error. ) comes before ( + return; + } + this.parenthesesCount -= 1; + break; + } + } + + private createPatternNode( + children: Token[], + negation: boolean = this.negationFlag + ): PatternNode { + const node: PatternNode = { + type: "pattern", + negation: negation, + children: children, + }; + this.negationFlag = false; + return node; + } +} From 199601175e78cbc7feb33880844d8060eda5366b Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Mon, 10 Nov 2025 23:09:29 +0100 Subject: [PATCH 02/18] fix logic, and impl basic error handling in the parser --- src/types/server.ts | 10 ++ src/utils/parsers/Oredic.ts | 237 +++++++++++++++++++++--------------- 2 files changed, 149 insertions(+), 98 deletions(-) diff --git a/src/types/server.ts b/src/types/server.ts index d56cd30..75cdfff 100644 --- a/src/types/server.ts +++ b/src/types/server.ts @@ -1,5 +1,6 @@ import { SystemPromptChoice } from "./grok"; import { EndpointConfig as ConfigEndpointConfig } from "../config/schema"; +import { PathLike } from "fs"; export type LogType = "success" | "info" | "warn" | "error" | "debug"; @@ -36,3 +37,12 @@ export type RequestEndpointConfig = { }; export type EndpointLevel = "source" | "main" | "child"; + +export type MiscError = { + code: number | null; + status: boolean; + send: boolean; + message: string | null; + location: PathLike | null; + time: Date | null; +}; diff --git a/src/utils/parsers/Oredic.ts b/src/utils/parsers/Oredic.ts index 1a7b057..985f9e8 100644 --- a/src/utils/parsers/Oredic.ts +++ b/src/utils/parsers/Oredic.ts @@ -3,7 +3,7 @@ * https://github.com/AE2-UEL/Applied-Energistics-2/blob/26bb5986c636e9bdde62559b0d1c2bbc48c4b9e3/src/main/java/appeng/util/item/OreDictFilterMatcher.java#L11 */ -import { getLogger } from "../Logger"; +import { MiscError } from "../../types/server"; type Ast = AstNode | null; @@ -27,49 +27,57 @@ type LexemeElement = { content: LexemeElement[] | string | null; }; +type ParseState = { + ast: Ast; + tokenBuffer: Token[]; + operandBuffer: AstNode[]; + currentOperator: "AND" | "XOR" | "OR" | null; + negationFlag: boolean; +}; + export class Ae2uelOredicParser { lexemeBuffer: string; parenthesesCount: number; - pos: 0; - - negationFlag: boolean; - currentOperator: "AND" | "XOR" | "OR" | null; + error: MiscError; constructor(private oredicString: string) { this.lexemeBuffer = ""; this.parenthesesCount = 0; - this.pos = 0; - - this.negationFlag = false; - this.currentOperator = null; + this.error = { + code: null, + status: false, + send: false, + message: null, + location: __dirname, + time: null, + }; } - parse() { - getLogger().simpleLog("debug", `parsing: ${this.oredicString}`); + parse(): Ast | MiscError { + const { lexemeList } = this.lexicalParse(this.oredicString.split("")); - const lexemeList = this.lexicalParse(this.oredicString.split("")); if (this.parenthesesCount !== 0) { - // Handle Error. Parantheses should be balanced + this.setError(400, "Unbalanced parentheses"); + return this.error; } const ast = this.parseNode(lexemeList); - getLogger().simpleLog( - "debug", - `Ast Parse: ${JSON.stringify(ast, undefined, 4)}` - ); + + if (this.error.status) { + return this.error; + } + return ast; } - /* - * This function iterates over the input once and makes an ordered list of lexeme - * elements. This groups every kind of node together in only one element, making the - * further parsing easier. - */ - private lexicalParse(pattern: string[]) { + private lexicalParse(pattern: string[]): { + lexemeList: LexemeElement[]; + consumed: number; + } { let lexemeList: LexemeElement[] = []; for (let i = 0; i < pattern.length; i++) { @@ -81,16 +89,16 @@ export class Ae2uelOredicParser { lexemeList = this.flushLexemeBuffer(lexemeList); this.updateParCount("START"); - const subList = this.lexicalParse(pattern.slice(i + 1)); - lexemeList.push({ type: "group", content: subList }); + const result = this.lexicalParse(pattern.slice(i + 1)); + lexemeList.push({ type: "group", content: result.lexemeList }); - i = this.pos + 1; + i += result.consumed; break; case ")": - this.flushLexemeBuffer(lexemeList); + lexemeList = this.flushLexemeBuffer(lexemeList); this.updateParCount("END"); - return lexemeList; + return { lexemeList, consumed: i + 1 }; case "&": lexemeList = this.flushLexemeBuffer(lexemeList); @@ -119,96 +127,100 @@ export class Ae2uelOredicParser { this.lexemeBuffer += pattern[i]; break; } - this.pos += 1; } this.flushLexemeBuffer(lexemeList); - return lexemeList; + return { lexemeList, consumed: pattern.length }; } - /* - * This is the function that actually parses the Lexeme List for the logic. - */ private parseNode(lexemeList: LexemeElement[]): Ast { - let ast: Ast = null; - let tokenBuffer: Token[] = []; - let operandBuffer: Array = []; + if (this.error.status) { + return null; + } + + const state: ParseState = { + ast: null, + tokenBuffer: [], + operandBuffer: [], + currentOperator: null, + negationFlag: false, + }; for (let i = 0; i < lexemeList.length; i++) { switch (lexemeList[i].type) { case "group": const subLexeme = lexemeList[i].content; if (typeof subLexeme === "string" || !subLexeme) { - // Handle Error. Isn't an array. + this.setError(400, "Invalid group content"); return null; } const subAst: Ast = this.parseNode(subLexeme); + if (this.error.status) { + return null; + } + if (!subAst) { - // Handle Error. Empty sub Ast + this.setError(400, "Failed to parse group"); return null; } - if (!ast) { - ast = subAst; - } else { - operandBuffer.push(subAst); + if (state.negationFlag) { + subAst.negation = true; + state.negationFlag = false; } + if (!state.ast) { + state.ast = subAst; + break; + } + + state.operandBuffer.push(subAst); break; case "operator": const operator = lexemeList[i].content; if (operator !== "AND" && operator !== "OR" && operator !== "XOR") { - // Handle Error. No valid operator + this.setError(400, "Invalid operator"); return null; } - if (tokenBuffer.length > 0) { - operandBuffer.push( - this.createPatternNode(tokenBuffer, this.negationFlag) - ); - tokenBuffer = []; - this.negationFlag = false; - } + this.flushTokens(state); - if (operandBuffer.length > 0) { - ast = this.flushOperatorNode(ast, operator, operandBuffer); - operandBuffer = []; + if (state.operandBuffer.length > 0 && state.currentOperator) { + this.flushOperands(state, state.currentOperator); } - this.currentOperator = operator; - + state.currentOperator = operator; break; case "negation": - if (tokenBuffer.length !== 0) { - // Handle Error. Negation in the middle of a text element + if (state.tokenBuffer.length !== 0) { + this.setError(400, "Negation must come before pattern tokens"); return null; } - - this.negationFlag = !this.negationFlag; + state.negationFlag = !state.negationFlag; break; case "wildcard": - tokenBuffer.push({ type: "wildcard" }); + state.tokenBuffer.push({ type: "wildcard" }); break; case "text": const content = lexemeList[i].content; if (typeof content !== "string") { - // Handle Error. Text Lexeme with no Content + this.setError(400, "Invalid text content"); return null; } - tokenBuffer.push({ type: "text", content: content }); + state.tokenBuffer.push({ type: "text", content: content }); break; } } - ast = this.flushEnd(ast, tokenBuffer, operandBuffer); - return ast; + + return this.flushEnd(state); } - private flushLexemeBuffer(lexemeList: LexemeElement[]) { + private flushLexemeBuffer(lexemeList: LexemeElement[]): LexemeElement[] { if (this.lexemeBuffer === "") { return lexemeList; } @@ -218,56 +230,78 @@ export class Ae2uelOredicParser { return lexemeList; } - private flushOperatorNode( - ast: Ast, - situation: "AND" | "XOR" | "OR", - content: AstNode[] - ): Ast { - if (!content) { - // Handle error. No content - return null; + private flushTokens(state: ParseState): void { + if (state.tokenBuffer.length > 0) { + state.operandBuffer.push( + this.createPatternNode(state.tokenBuffer, state.negationFlag) + ); + state.tokenBuffer = []; + state.negationFlag = false; } + } - if (!ast) { - return content[0]; + private flushOperands( + state: ParseState, + operator: "AND" | "XOR" | "OR" + ): void { + if (state.operandBuffer.length === 0) { + return; } - if (ast.type === "pattern" || this.currentOperator !== situation) { - return { + if (!state.ast) { + if (state.operandBuffer.length === 1) { + state.ast = state.operandBuffer[0]; + state.operandBuffer = []; + return; + } + + state.ast = { type: "operator", - operator: situation, + operator: operator, negation: false, - children: [ast].concat(content), + children: state.operandBuffer, }; + state.operandBuffer = []; + return; } - if (this.currentOperator === situation) { - ast.children = ast.children.concat(content); - return ast; + if (state.ast.type === "operator" && state.ast.operator === operator) { + state.ast.children = state.ast.children.concat(state.operandBuffer); + state.operandBuffer = []; + return; } - return ast; + state.ast = { + type: "operator", + operator: operator, + negation: false, + children: [state.ast].concat(state.operandBuffer), + }; + state.operandBuffer = []; } - private flushEnd( - ast: Ast, - tokenBuffer: Token[], - operandBuffer: AstNode[] - ): Ast { - if (tokenBuffer.length > 0) { - operandBuffer.push( - this.createPatternNode(tokenBuffer, this.negationFlag) + private flushEnd(state: ParseState): Ast { + if (state.tokenBuffer.length > 0) { + const pattern = this.createPatternNode( + state.tokenBuffer, + state.negationFlag ); + + if (!state.ast && !state.currentOperator) { + return pattern; + } + + state.operandBuffer.push(pattern); } - if (operandBuffer.length > 0 && this.currentOperator) { - ast = this.flushOperatorNode(ast, this.currentOperator, operandBuffer); + if (state.operandBuffer.length > 0 && state.currentOperator) { + this.flushOperands(state, state.currentOperator); } - return ast; + return state.ast; } - private updateParCount(side: "START" | "END") { + private updateParCount(side: "START" | "END"): void { switch (side) { case "START": this.parenthesesCount += 1; @@ -275,7 +309,7 @@ export class Ae2uelOredicParser { case "END": if (this.parenthesesCount === 0) { - // Handle Error. ) comes before ( + this.setError(400, "Closing parenthesis without opening"); return; } this.parenthesesCount -= 1; @@ -285,14 +319,21 @@ export class Ae2uelOredicParser { private createPatternNode( children: Token[], - negation: boolean = this.negationFlag + negation: boolean = false ): PatternNode { const node: PatternNode = { type: "pattern", negation: negation, children: children, }; - this.negationFlag = false; return node; } + + private setError(code: number, message: string): void { + this.error.code = code; + this.error.status = true; + this.error.send = true; + this.error.message = message; + this.error.time = new Date(); + } } From 78ac71079a124d53bedf9af482974bef5eb732fd Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Mon, 10 Nov 2025 23:18:36 +0100 Subject: [PATCH 03/18] change endpoint --- src/routes/index.ts | 8 ++++++++ src/routes/leveret/leveret.ts | 8 +------- src/routes/{leveret => util}/oredic/oredic-ae2.ts | 0 src/routes/util/util.ts | 15 +++++++++++++++ 4 files changed, 24 insertions(+), 7 deletions(-) rename src/routes/{leveret => util}/oredic/oredic-ae2.ts (100%) create mode 100644 src/routes/util/util.ts diff --git a/src/routes/index.ts b/src/routes/index.ts index f802abd..5af84fd 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -6,6 +6,7 @@ import { jsonWithRawBody } from "../middleware/jsonWithRawBody"; import { initEndpointHandler } from "../utils/Endpoint"; import { setEndpointData } from "../middleware/setEndpointData"; import { dataRoutes } from "./data/data"; +import { utilRoutes } from "./util/util"; export function routes() { const router = Router(); @@ -18,5 +19,12 @@ export function routes() { ); router.use("/data", setEndpointData("source", "data"), dataRoutes()); + + router.use( + "/util", + jsonWithRawBody(), + setEndpointData("source", "util"), + utilRoutes() + ); return router; } diff --git a/src/routes/leveret/leveret.ts b/src/routes/leveret/leveret.ts index f2ef874..f37734b 100644 --- a/src/routes/leveret/leveret.ts +++ b/src/routes/leveret/leveret.ts @@ -1,7 +1,7 @@ import { Router } from "express"; import { grokRoutes } from "./grok/grok"; import { setEndpointData } from "../../middleware/setEndpointData"; -import { oredicRoutes } from "./oredic/oredic-ae2"; +import { oredicRoutes } from "../util/oredic/oredic-ae2"; export function leveretRoutes() { const router = Router(); @@ -14,11 +14,5 @@ export function leveretRoutes() { grokRoutes() ); - router.use( - "/oredic-ae2", - setEndpointData("main", "oredic-ae2"), - oredicRoutes() - ); - return router; } diff --git a/src/routes/leveret/oredic/oredic-ae2.ts b/src/routes/util/oredic/oredic-ae2.ts similarity index 100% rename from src/routes/leveret/oredic/oredic-ae2.ts rename to src/routes/util/oredic/oredic-ae2.ts diff --git a/src/routes/util/util.ts b/src/routes/util/util.ts new file mode 100644 index 0000000..15d81b7 --- /dev/null +++ b/src/routes/util/util.ts @@ -0,0 +1,15 @@ +import { Router } from "express"; +import { setEndpointData } from "../../middleware/setEndpointData"; +import { oredicRoutes } from "./oredic/oredic-ae2"; + +export function utilRoutes() { + const router = Router(); + + router.use( + "/oredic-ae2", + setEndpointData("main", "oredic-ae2"), + oredicRoutes() + ); + + return router; +} From a61b5c0c3d2a1d8ee33ddf026f101f1cb60aaa4e Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Wed, 12 Nov 2025 13:36:41 +0100 Subject: [PATCH 04/18] add part of the flattening --- src/controllers/GrokController.ts | 6 +- src/controllers/MembersController.ts | 6 +- src/server.ts | 3 +- src/utils/Logger.ts | 4 +- src/utils/Timer.ts | 67 +++++- src/utils/parsers/Oredic.ts | 291 ++++++++++++++++++++++++++- 6 files changed, 361 insertions(+), 16 deletions(-) diff --git a/src/controllers/GrokController.ts b/src/controllers/GrokController.ts index 2f00c4f..cb22cb5 100644 --- a/src/controllers/GrokController.ts +++ b/src/controllers/GrokController.ts @@ -59,13 +59,13 @@ export class GrokController { endpointName ); - const time_taken_ms = stopTimer("grok-req").getTime(); + const time_taken = stopTimer("grok-req").getTime("ms", 0); getLogger().simpleLog( "info", - `Served Request on ${endpointConfig.endpointName} in ${time_taken_ms}ms` + `Served Request on ${endpointConfig.endpointName} in ${time_taken.formatted}` ); - res.json({ completion: completion, duration: time_taken_ms }); + res.json({ completion: completion, duration: time_taken.adjusted }); } catch (err) { next(err); } diff --git a/src/controllers/MembersController.ts b/src/controllers/MembersController.ts index 9e4579d..6c5136a 100644 --- a/src/controllers/MembersController.ts +++ b/src/controllers/MembersController.ts @@ -68,12 +68,12 @@ export class MembersController { members = await this.getUsernamesAndIds(); break; } - const time_taken_ms = stopTimer("members-fetch").getTime(); + const time_taken_ms = stopTimer("members-fetch").getTime("ms", 0); getLogger().simpleLog( "info", - `Served Request on ${endpointConfig.main} in ${time_taken_ms}ms` + `Served Request on ${endpointConfig.main} in ${time_taken_ms.formatted}` ); - res.json({ members: members, duration: time_taken_ms }); + res.json({ members: members, duration: time_taken_ms.adjusted }); } catch (err) { next(err); } diff --git a/src/server.ts b/src/server.ts index e291574..fafe8b9 100644 --- a/src/server.ts +++ b/src/server.ts @@ -43,10 +43,9 @@ async function main() { "info", `Server is running at http://${config.RUNNING_IP}:${config.PORT}` ); - const main_timeTaken_ms = stopTimer("main").getTime(); getLogger().simpleLog( "info", - `Server took ${main_timeTaken_ms}ms to start` + `Server took ${stopTimer("main").getTime("auto", 3).formatted} to start` ); }); } diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index 3130569..4218794 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -10,7 +10,7 @@ export class Logger { this.name = config.LOGGER_NAME; } - static colors = { + private static colors = { success: "\x1b[32m", // Green info: "\x1b[36m", // Cyan warn: "\x1b[33m", // Yellow @@ -45,7 +45,7 @@ export class Logger { console.log(log); } - getDateTime() { + private getDateTime() { const rawTimestamp = new Date(); return rawTimestamp.toISOString(); } diff --git a/src/utils/Timer.ts b/src/utils/Timer.ts index db7df16..5729cc6 100644 --- a/src/utils/Timer.ts +++ b/src/utils/Timer.ts @@ -1,14 +1,69 @@ let timers: Map = new Map(); export class Timer { - startTime; + startTime: number; constructor() { - this.startTime = Date.now(); + this.startTime = performance.now(); } - getTime() { - return Date.now() - this.startTime; + getTime( + unit: "micro" | "ms" | "s" | "m" | "auto" = "auto", + precision: number = 2 + ): { + raw: number; + adjusted: number; + formatted: string; + } { + const timeTaken_ms = performance.now() - this.startTime; + let adjustedTime: number = 0; + let unitLabel: string = ""; + + switch (unit) { + case "auto": + if (timeTaken_ms < 1) { + adjustedTime = timeTaken_ms * 1000; + unitLabel = "μs"; + } else if (timeTaken_ms < 1000) { + adjustedTime = timeTaken_ms; + unitLabel = "ms"; + } else if (timeTaken_ms < 60000) { + adjustedTime = timeTaken_ms / 1000; + unitLabel = "s"; + } else { + adjustedTime = timeTaken_ms / 60000; + unitLabel = "m"; + } + break; + + case "micro": + adjustedTime = timeTaken_ms * 1000; + unitLabel = "μs"; + break; + + case "ms": + adjustedTime = timeTaken_ms; + unitLabel = "ms"; + break; + + case "s": + adjustedTime = timeTaken_ms / 1000; + unitLabel = "s"; + break; + + case "m": + adjustedTime = timeTaken_ms / 60000; + unitLabel = "m"; + break; + } + + const formattedTime = `${adjustedTime.toFixed(precision)}${unitLabel}`; + + return { + raw: timeTaken_ms, + adjusted: adjustedTime, + formatted: formattedTime, + }; } } @@ -25,3 +80,7 @@ export function stopTimer(id: string): Timer { timers.delete(id); return time; } + +export function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/src/utils/parsers/Oredic.ts b/src/utils/parsers/Oredic.ts index 985f9e8..7367cfa 100644 --- a/src/utils/parsers/Oredic.ts +++ b/src/utils/parsers/Oredic.ts @@ -4,6 +4,8 @@ */ import { MiscError } from "../../types/server"; +import { getLogger } from "../Logger"; +import { startTimer, stopTimer, Timer } from "../Timer"; type Ast = AstNode | null; @@ -13,13 +15,30 @@ type PatternNode = { negation: boolean; children: Array; }; -type OperatorNode = { + +type AndNode = { type: "operator"; - operator: "AND" | "OR" | "XOR"; + operator: "AND"; negation: boolean; children: Array; }; +type OrNode = { + type: "operator"; + operator: "OR"; + negation: boolean; + children: Array; +}; + +type XorNode = { + type: "operator"; + operator: "XOR"; + negation: boolean; + children: Array; +}; + +type OperatorNode = AndNode | OrNode | XorNode; + type Token = { type: "text"; content: string } | { type: "wildcard" }; type LexemeElement = { @@ -42,6 +61,8 @@ export class Ae2uelOredicParser { error: MiscError; + private optimizationPipeline: Array<(node: AstNode) => void>; + constructor(private oredicString: string) { this.lexemeBuffer = ""; @@ -55,9 +76,17 @@ export class Ae2uelOredicParser { location: __dirname, time: null, }; + + this.optimizationPipeline = [ + this.applyAssociativity.bind(this), + this.applyAnnihilatorXor.bind(this), + this.applyDeMorgan.bind(this), + this.applyWildcardConjunction.bind(this), + ]; } parse(): Ast | MiscError { + startTimer("parser"); const { lexemeList } = this.lexicalParse(this.oredicString.split("")); if (this.parenthesesCount !== 0) { @@ -66,14 +95,42 @@ export class Ae2uelOredicParser { } const ast = this.parseNode(lexemeList); + const badAst = JSON.stringify(ast); + + if (this.error.status) { + return this.error; + } + + getLogger().formattingLog("Bad AST"); + getLogger().simpleLog("debug", JSON.stringify(ast, undefined, 4)); + + this.flattenAst(ast); if (this.error.status) { return this.error; } + getLogger().formattingLog("Good AST"); + getLogger().simpleLog("debug", JSON.stringify(ast, undefined, 4)); + + getLogger().formattingLog("Results:"); + getLogger().simpleLog("debug", `Input: ${this.oredicString}`); + + getLogger().simpleLog( + "debug", + `Time taken: ${stopTimer("parser").getTime().formatted}` + ); + getLogger().simpleLog( + "debug", + `Changed?: ${JSON.stringify(ast) !== badAst}` + ); return ast; } + /* + * Main methods + */ + private lexicalParse(pattern: string[]): { lexemeList: LexemeElement[]; consumed: number; @@ -220,6 +277,35 @@ export class Ae2uelOredicParser { return this.flushEnd(state); } + private flattenAst(ast: Ast): void { + if (!ast) { + return; + } + + let currentAst = ast; + let previousAst: Ast = null; + + while (JSON.stringify(currentAst) !== JSON.stringify(previousAst)) { + previousAst = JSON.parse(JSON.stringify(currentAst)); + + for (const fn of this.optimizationPipeline) { + fn(currentAst); + } + + if (currentAst.type === "pattern") return; + + for (const child of currentAst.children) { + this.flattenAst(child); + } + + if (this.error.status) return; + } + } + + /* + * Helper methods + */ + private flushLexemeBuffer(lexemeList: LexemeElement[]): LexemeElement[] { if (this.lexemeBuffer === "") { return lexemeList; @@ -329,6 +415,199 @@ export class Ae2uelOredicParser { return node; } + /* + * Optimization methods + */ + + // Associativity (OR, AND, XOR): OR: (a, OR: (b, c)) = OR: (a, b, c) + private applyAssociativity(node: AstNode): void { + if (!this.operatorAccepted(node, "all")) return; + const operatorNode = node as OperatorNode; + + const topLevelOperator = operatorNode.operator; + const newChildren: AstNode[] = []; + + for (let i = 0; i < operatorNode.children.length; i++) { + const child = operatorNode.children[i]; + if ( + child.type === "operator" && + child.operator === topLevelOperator && + child.negation === false + ) { + for (const subChild of child.children) { + newChildren.push(subChild); + } + } else { + newChildren.push(child); + } + } + + node.children = newChildren; + } + + // Distributivity of AND over OR: OR: (AND: (a, b), AND: (a, c)) = AND: (a, OR: (b, c)) + // Distributivity of OR over AND: AND: (OR: (a, b), OR: (a, c)) = OR: (a, AND: (b, c)) + private applyDistributivity(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND", "OR"])) return; + const operatorNode = node as OperatorNode; + } + + // Identity for OR: OR: (a, false) = a + private applyIdentityOr(node: AstNode): void { + if (!this.operatorAccepted(node, ["OR"])) return; + const operatorNode = node as OperatorNode; + } + + // Identity for AND: AND: (a, true) = a + private applyIdentityAnd(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND"])) return; + const operatorNode = node as OperatorNode; + } + + // Identity for XOR: XOR: (a, false) = a + private applyIdentityXor(node: AstNode): void { + if (!this.operatorAccepted(node, ["XOR"])) return; + const operatorNode = node as OperatorNode; + } + + // Annihilator for OR: OR: (a, true) = true + private applyAnnihilatorOr(node: AstNode): void { + if (!this.operatorAccepted(node, ["OR"])) return; + const operatorNode = node as OperatorNode; + } + + // Annihilator for AND: AND: (a, false) = false => error + private applyAnnihilatorAnd(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND"])) return; + const operatorNode = node as OperatorNode; + } + + // Annihilator for XOR: XOR: (a, a) = false => error + private applyAnnihilatorXor(node: AstNode): void { + if (!this.operatorAccepted(node, ["XOR"])) return; + const operatorNode = node as OperatorNode; + + const uniqueChildren = new Map(); + + for (const child of operatorNode.children) { + const hash = JSON.stringify(child); + if (uniqueChildren.has(hash)) { + this.setWarn( + "Invalid Logic: XOR has two identical elements. XOR(a,a) is always false" + ); + return; + } + uniqueChildren.set(hash, true); + } + } + + // Idempotence (OR, AND): OR: (a, a) = a + private applyIdempotence(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND", "OR"])) return; + const operatorNode = node as OperatorNode; + } + + // Absorption (OR, AND): AND: (a, OR: (a, b)) = a + private applyAbsorption(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND", "OR"])) return; + const operatorNode = node as OperatorNode; + } + + // Complementation for AND: AND: (a, NOT(a)) = false => error + private applyComplementationAnd(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND"])) return; + const operatorNode = node as OperatorNode; + } + + // Complementation for OR: OR: (a, NOT(a)) = true + private applyComplementationOr(node: AstNode): void { + if (!this.operatorAccepted(node, ["OR"])) return; + const operatorNode = node as OperatorNode; + } + + // Complementary for XOR: XOR(a, true) = NOT(a), XOR(a, NOT(a)) = true + // Complementation for OR: OR: (a, NOT(a)) = true + private applyComplementaryOrs(node: AstNode): void { + if (!this.operatorAccepted(node, ["XOR"])) return; + const operatorNode = node as OperatorNode; + } + + // Double negation: NOT(NOT(a)) = a + // What's actually useful here to save a single char NOT(AND: (NOT(a), b)) = AND: (a, NOT(b)) + private applyDoubleNegation(node: AstNode): void { + if (!this.operatorAccepted(node, "all")) return; + const operatorNode = node as OperatorNode; + const counts: { positive: number; negative: number } = { + positive: 0, + negative: 0, + }; + + if (!operatorNode.negation) return; + for (const child of operatorNode.children) { + if (child.negation) { + counts.negative += 1; + } else { + counts.positive += 1; + } + } + + // The objective is to save chars, so this is why we compare the amounts of positive nodes vs. + // negative nodes. We add 2 because this may add character overhead via adding a group + if (counts.negative > counts.positive + 2) { + for (const child of operatorNode.children) { + child.negation = !child.negation; + } + } + + node = operatorNode; + } + + // De Morgan's laws: OR: (NOT(a), NOT(b)) = NOT(AND: (a, b)) + private applyDeMorgan(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND", "OR"])) return; + const operatorNode = node as OperatorNode; + + let allNegation = true; + + for (const child of operatorNode.children) { + if (!child.negation) allNegation = false; + } + + if (allNegation) { + const complemetary = operatorNode.operator === "OR" ? "AND" : "OR"; + operatorNode.negation = true; + operatorNode.operator = complemetary; + operatorNode.children.forEach((child) => { + child.negation = false; + }); + } + + node = operatorNode; + } + + // Conjunction for wildcards: Token(a, wildcard, wildcard, b) = Token(a, wildcard, b) + private applyWildcardConjunction(node: AstNode): void { + if (node.type !== "pattern") return; + for (let i = 1; i < node.children.length; i++) { + const lastChild = node.children[i - 1]; + const currChild = node.children[i]; + if (currChild.type === "wildcard" && lastChild.type === "wildcard") { + node.children.splice(i, 1); + i -= 1; + } + } + } + + // Helper to check if an operator is in the list of accepted operators + private operatorAccepted( + node: AstNode, + acceptedOperators: Array<"AND" | "OR" | "XOR"> | "all" + ): boolean { + if (node.type === "pattern") return false; + if (acceptedOperators === "all") return true; + return acceptedOperators.includes(node.operator); + } + private setError(code: number, message: string): void { this.error.code = code; this.error.status = true; @@ -336,4 +615,12 @@ export class Ae2uelOredicParser { this.error.message = message; this.error.time = new Date(); } + + private setWarn(message: string): void { + this.error.code = 200; + this.error.status = true; + this.error.send = true; + this.error.message = message; + this.error.time = new Date(); + } } From 66168b6f25d309afbe1684b4d4ade2710def298b Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Wed, 12 Nov 2025 13:39:29 +0100 Subject: [PATCH 05/18] forgor to apply double negation --- src/utils/parsers/Oredic.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/parsers/Oredic.ts b/src/utils/parsers/Oredic.ts index 7367cfa..f3f2929 100644 --- a/src/utils/parsers/Oredic.ts +++ b/src/utils/parsers/Oredic.ts @@ -80,6 +80,7 @@ export class Ae2uelOredicParser { this.optimizationPipeline = [ this.applyAssociativity.bind(this), this.applyAnnihilatorXor.bind(this), + this.applyDoubleNegation.bind(this), this.applyDeMorgan.bind(this), this.applyWildcardConjunction.bind(this), ]; From 9a27a4dab8899df71076293c5bfed373a57bade6 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Wed, 12 Nov 2025 13:48:08 +0100 Subject: [PATCH 06/18] forgor to make applyDoubleNegation change the root op's node --- src/utils/parsers/Oredic.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/parsers/Oredic.ts b/src/utils/parsers/Oredic.ts index f3f2929..d59bf7f 100644 --- a/src/utils/parsers/Oredic.ts +++ b/src/utils/parsers/Oredic.ts @@ -555,6 +555,7 @@ export class Ae2uelOredicParser { // The objective is to save chars, so this is why we compare the amounts of positive nodes vs. // negative nodes. We add 2 because this may add character overhead via adding a group if (counts.negative > counts.positive + 2) { + operatorNode.negation = false; for (const child of operatorNode.children) { child.negation = !child.negation; } From e503aad8c4728cb5875be1d1d4acfb45e0334986 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Wed, 12 Nov 2025 13:51:07 +0100 Subject: [PATCH 07/18] nevermind this double negation was actually wrong logic --- src/utils/parsers/Oredic.ts | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/utils/parsers/Oredic.ts b/src/utils/parsers/Oredic.ts index d59bf7f..e9ddf66 100644 --- a/src/utils/parsers/Oredic.ts +++ b/src/utils/parsers/Oredic.ts @@ -534,34 +534,9 @@ export class Ae2uelOredicParser { } // Double negation: NOT(NOT(a)) = a - // What's actually useful here to save a single char NOT(AND: (NOT(a), b)) = AND: (a, NOT(b)) private applyDoubleNegation(node: AstNode): void { if (!this.operatorAccepted(node, "all")) return; const operatorNode = node as OperatorNode; - const counts: { positive: number; negative: number } = { - positive: 0, - negative: 0, - }; - - if (!operatorNode.negation) return; - for (const child of operatorNode.children) { - if (child.negation) { - counts.negative += 1; - } else { - counts.positive += 1; - } - } - - // The objective is to save chars, so this is why we compare the amounts of positive nodes vs. - // negative nodes. We add 2 because this may add character overhead via adding a group - if (counts.negative > counts.positive + 2) { - operatorNode.negation = false; - for (const child of operatorNode.children) { - child.negation = !child.negation; - } - } - - node = operatorNode; } // De Morgan's laws: OR: (NOT(a), NOT(b)) = NOT(AND: (a, b)) From 4b40854d9cda6ef51dfd3cbb93c79e1ee84306aa Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Thu, 13 Nov 2025 03:32:51 +0100 Subject: [PATCH 08/18] add logic to transform pattern nodes into regexes --- src/controllers/OredicController.ts | 16 +- src/loaders/files.ts | 9 +- src/loaders/storage.ts | 6 + src/types/parsing.ts | 63 + src/types/server.ts | 1 + src/utils/parsers/{Oredic.ts => OredicAe2.ts} | 49 +- src/utils/parsers/OredicMatcher.ts | 58 + src/utils/parsers/OredicSubstring.ts | 5 + storage/oredic/dumps/nomi-ceu.txt | 7444 +++++++++++++++++ 9 files changed, 7607 insertions(+), 44 deletions(-) create mode 100644 src/types/parsing.ts rename src/utils/parsers/{Oredic.ts => OredicAe2.ts} (94%) create mode 100644 src/utils/parsers/OredicMatcher.ts create mode 100644 src/utils/parsers/OredicSubstring.ts create mode 100644 storage/oredic/dumps/nomi-ceu.txt diff --git a/src/controllers/OredicController.ts b/src/controllers/OredicController.ts index 9d0dd03..dc2ced2 100644 --- a/src/controllers/OredicController.ts +++ b/src/controllers/OredicController.ts @@ -1,13 +1,25 @@ import { RequestHandler, Request, Response, NextFunction } from "express"; -import { Ae2uelOredicParser } from "../utils/parsers/Oredic"; +import { Ae2uelOredicParser } from "../utils/parsers/OredicAe2"; import { getLogger } from "../utils/Logger"; +import { OredicMatcher } from "../utils/parsers/OredicMatcher"; export class OredicController { constructor() {} async answer(req: Request) { const Parser = new Ae2uelOredicParser(req.body.string); - return Parser.parse(); + const rules = Parser.parse(); + if (!rules) { + // Catch unknown error + return; + } + if (rules.type === "error") { + // Catch error according to the metadata + return; + } + + const Matcher = new OredicMatcher(rules, "nomi-ceu"); + return Matcher.ast; } handler = diff --git a/src/loaders/files.ts b/src/loaders/files.ts index 793a387..0ed7dba 100644 --- a/src/loaders/files.ts +++ b/src/loaders/files.ts @@ -13,10 +13,17 @@ export const HOGICHAN_PROMPT_FILE = path.join(PROMPTS_DIR, "hogichan.txt"); export const NOMICORD_PROMPT_FILE = path.join(PROMPTS_DIR, "nomicord.txt"); export const FILTERS_FILES_DIR = path.join(STORAGE_DIR, "filters"); -export let FILTERS_FILES = new Map(); +export const FILTERS_FILES = new Map(); fs.readdirSync(FILTERS_FILES_DIR).forEach((file) => FILTERS_FILES.set( file.replace(".json", ""), path.join(FILTERS_FILES_DIR, file) ) ); + +export const OREDIC_DIR = path.join(STORAGE_DIR, "oredic"); +export const DUMPS_DIR = path.join(OREDIC_DIR, "dumps"); +export const DUMPS_FILES = new Map(); +fs.readdirSync(DUMPS_DIR).forEach((file) => + DUMPS_FILES.set(file.replace(".txt", ""), path.join(DUMPS_DIR, file)) +); diff --git a/src/loaders/storage.ts b/src/loaders/storage.ts index 755c491..bb6b72d 100644 --- a/src/loaders/storage.ts +++ b/src/loaders/storage.ts @@ -2,6 +2,7 @@ import fs from "fs"; import { CONFIG_FILE, DEFAULT_PROMPT_FILE, + DUMPS_FILES, FILTERS_CONFIG_FILE, HOGICHAN_PROMPT_FILE, NOMICORD_PROMPT_FILE, @@ -17,3 +18,8 @@ export const HOGICHAN_PROMPT = DEFAULT_PROMPT + "\n" + fs.readFileSync(HOGICHAN_PROMPT_FILE, "utf-8"); export const NOMICORD_PROMPT = DEFAULT_PROMPT + "\n" + fs.readFileSync(NOMICORD_PROMPT_FILE, "utf-8"); + +export const DUMPS = new Map(); +DUMPS_FILES.forEach((file, fileName) => { + DUMPS.set(fileName, fs.readFileSync(file, "utf-8")); +}); diff --git a/src/types/parsing.ts b/src/types/parsing.ts new file mode 100644 index 0000000..126be15 --- /dev/null +++ b/src/types/parsing.ts @@ -0,0 +1,63 @@ +export type Ast = AstNode | null; +export type exAst = exAstNode | null; + +export type AstNode = PatternNode | OperatorNode; +export type exAstNode = RegexNode | exOperatorNode | AstNode; + +export type PatternNode = { + type: "pattern"; + negation: boolean; + children: Array; +}; + +export type RegexNode = { + type: "regex"; + negation: boolean; + child: RegExp; +}; + +export type OperatorNode = AndNode | OrNode | XorNode; + +export type AndNode = { + type: "operator"; + operator: "AND"; + negation: boolean; + children: Array; +}; +export type OrNode = { + type: "operator"; + operator: "OR"; + negation: boolean; + children: Array; +}; +export type XorNode = { + type: "operator"; + operator: "XOR"; + negation: boolean; + children: Array; +}; + +export type exOperatorNode = exAndNode | exOrNode | exXorNode; + +export type exAndNode = { + type: "operator"; + operator: "AND"; + negation: boolean; + children: Array; +}; +export type exOrNode = { + type: "operator"; + operator: "OR"; + negation: boolean; + children: Array; +}; +export type exXorNode = { + type: "operator"; + operator: "XOR"; + negation: boolean; + children: Array; +}; + +export type Token = { type: "text"; content: string } | { type: "wildcard" }; + +export type SupportedPack = "nomi-ceu"; diff --git a/src/types/server.ts b/src/types/server.ts index 75cdfff..ec2fadc 100644 --- a/src/types/server.ts +++ b/src/types/server.ts @@ -39,6 +39,7 @@ export type RequestEndpointConfig = { export type EndpointLevel = "source" | "main" | "child"; export type MiscError = { + type: "error"; code: number | null; status: boolean; send: boolean; diff --git a/src/utils/parsers/Oredic.ts b/src/utils/parsers/OredicAe2.ts similarity index 94% rename from src/utils/parsers/Oredic.ts rename to src/utils/parsers/OredicAe2.ts index e9ddf66..e74c989 100644 --- a/src/utils/parsers/Oredic.ts +++ b/src/utils/parsers/OredicAe2.ts @@ -3,44 +3,17 @@ * https://github.com/AE2-UEL/Applied-Energistics-2/blob/26bb5986c636e9bdde62559b0d1c2bbc48c4b9e3/src/main/java/appeng/util/item/OreDictFilterMatcher.java#L11 */ +import { + Ast, + AstNode, + PatternNode, + OperatorNode, + Token, +} from "../../types/parsing"; import { MiscError } from "../../types/server"; import { getLogger } from "../Logger"; import { startTimer, stopTimer, Timer } from "../Timer"; -type Ast = AstNode | null; - -type AstNode = PatternNode | OperatorNode; -type PatternNode = { - type: "pattern"; - negation: boolean; - children: Array; -}; - -type AndNode = { - type: "operator"; - operator: "AND"; - negation: boolean; - children: Array; -}; - -type OrNode = { - type: "operator"; - operator: "OR"; - negation: boolean; - children: Array; -}; - -type XorNode = { - type: "operator"; - operator: "XOR"; - negation: boolean; - children: Array; -}; - -type OperatorNode = AndNode | OrNode | XorNode; - -type Token = { type: "text"; content: string } | { type: "wildcard" }; - type LexemeElement = { type: "group" | "operator" | "negation" | "wildcard" | "text"; content: LexemeElement[] | string | null; @@ -69,6 +42,7 @@ export class Ae2uelOredicParser { this.parenthesesCount = 0; this.error = { + type: "error", code: null, status: false, send: false, @@ -80,7 +54,6 @@ export class Ae2uelOredicParser { this.optimizationPipeline = [ this.applyAssociativity.bind(this), this.applyAnnihilatorXor.bind(this), - this.applyDoubleNegation.bind(this), this.applyDeMorgan.bind(this), this.applyWildcardConjunction.bind(this), ]; @@ -102,18 +75,12 @@ export class Ae2uelOredicParser { return this.error; } - getLogger().formattingLog("Bad AST"); - getLogger().simpleLog("debug", JSON.stringify(ast, undefined, 4)); - this.flattenAst(ast); if (this.error.status) { return this.error; } - getLogger().formattingLog("Good AST"); - getLogger().simpleLog("debug", JSON.stringify(ast, undefined, 4)); - getLogger().formattingLog("Results:"); getLogger().simpleLog("debug", `Input: ${this.oredicString}`); diff --git a/src/utils/parsers/OredicMatcher.ts b/src/utils/parsers/OredicMatcher.ts new file mode 100644 index 0000000..7fb15e2 --- /dev/null +++ b/src/utils/parsers/OredicMatcher.ts @@ -0,0 +1,58 @@ +import { + AstNode, + PatternNode, + RegexNode, + SupportedPack, + exAst, +} from "../../types/parsing"; +import { getLogger } from "../Logger"; + +export class OredicMatcher { + validOredics: string[]; + ast: exAst; + + constructor( + private rules: AstNode, + private pack: SupportedPack + ) { + this.ast = rules; + this.validOredics = this.parse(this.ast); + } + + parse(ast: exAst): string[] { + this.parsePatterns(ast); + // 2nd parse: match logic to oredics. Return array of valid oredics + return []; + } + + private parsePatterns(ast: exAst): void { + if (!ast || ast.type === "regex") return; + + if (ast.type === "pattern") { + const regexNode = this.createRegex(ast); + delete (ast as any).children; + Object.assign(ast, regexNode); + return; + } + + for (const child of ast.children) { + this.parsePatterns(child); + } + } + + private createRegex(patternNode: PatternNode): RegexNode { + let pattern = ""; + for (const child of patternNode.children) { + if (child.type === "wildcard") { + pattern += "[a-zA-Z]+"; + } else { + pattern += child.content; + } + } + return { + type: "regex", + negation: patternNode.negation, + child: new RegExp(pattern), + }; + } +} diff --git a/src/utils/parsers/OredicSubstring.ts b/src/utils/parsers/OredicSubstring.ts new file mode 100644 index 0000000..1fded55 --- /dev/null +++ b/src/utils/parsers/OredicSubstring.ts @@ -0,0 +1,5 @@ +import { SupportedPack } from "../../types/parsing"; + +export class OredicSubstrings { + constructor(private pack: SupportedPack) {} +} diff --git a/storage/oredic/dumps/nomi-ceu.txt b/storage/oredic/dumps/nomi-ceu.txt new file mode 100644 index 0000000..ff0269b --- /dev/null +++ b/storage/oredic/dumps/nomi-ceu.txt @@ -0,0 +1,7444 @@ +logWood +plankWood +slabWood +stairWood +fenceWood +fenceGateWood +doorWood +stickWood +treeSapling +treeLeaves +vine +oreGold +oreIron +oreLapis +oreDiamond +oreRedstone +oreEmerald +oreQuartz +oreCoal +ingotIron +ingotGold +ingotBrick +ingotBrickNether +nuggetGold +nuggetIron +gemDiamond +gemEmerald +gemQuartz +gemPrismarine +dustPrismarine +dustRedstone +dustGlowstone +gemLapis +blockGold +blockIron +blockLapis +blockDiamond +blockRedstone +blockEmerald +blockQuartz +blockCoal +cropWheat +cropPotato +cropCarrot +cropNetherWart +sugarcane +blockCactus +dye +paper +slimeball +enderpearl +bone +gunpowder +string +netherStar +leather +feather +egg +record +dirt +grass +stone +cobblestone +gravel +sand +sandstone +netherrack +obsidian +glowstone +endstone +torch +workbench +blockSlime +blockPrismarine +blockPrismarineBrick +blockPrismarineDark +stoneGranite +stoneGranitePolished +stoneDiorite +stoneDioritePolished +stoneAndesite +stoneAndesitePolished +blockGlassColorless +blockGlass +paneGlassColorless +paneGlass +wool +chest +chestWood +chestEnder +chestTrapped +dyeBlack +woolBlack +blockGlassBlack +paneGlassBlack +dyeRed +woolRed +blockGlassRed +paneGlassRed +dyeGreen +woolGreen +blockGlassGreen +paneGlassGreen +dyeBrown +woolBrown +blockGlassBrown +paneGlassBrown +dyeBlue +woolBlue +blockGlassBlue +paneGlassBlue +dyePurple +woolPurple +blockGlassPurple +paneGlassPurple +dyeCyan +woolCyan +blockGlassCyan +paneGlassCyan +dyeLightGray +woolLightGray +blockGlassLightGray +paneGlassLightGray +dyeGray +woolGray +blockGlassGray +paneGlassGray +dyePink +woolPink +blockGlassPink +paneGlassPink +dyeLime +woolLime +blockGlassLime +paneGlassLime +dyeYellow +woolYellow +blockGlassYellow +paneGlassYellow +dyeLightBlue +woolLightBlue +blockGlassLightBlue +paneGlassLightBlue +dyeMagenta +woolMagenta +blockGlassMagenta +paneGlassMagenta +dyeOrange +woolOrange +blockGlassOrange +paneGlassOrange +dyeWhite +woolWhite +blockGlassWhite +paneGlassWhite +blockGlowstone +blockWool +coal +charcoal +dustBlaze +rodBlaze +cropBeetroot +cropMelon +cropPumpkin +seedBeetroot +seedMelon +seedPumpkin +seedWheat +cofh:potion +dyeSilver +slabWoodOak +slabWoodSpruce +slabWoodBirch +slabWoodJungle +slabWoodAcacia +slabWoodDarkOak +slabStone +slabSandstone +slabCobblestone +slabBricks +slabStoneBricks +slabNetherBrick +slabQuartz +blockWoolWhite +blockWoolOrange +blockWoolMagenta +blockWoolLightBlue +blockWoolYellow +blockWoolLime +blockWoolPink +blockWoolGray +blockWoolLightGray +blockWoolCyan +blockWoolPurple +blockWoolBlue +blockWoolBrown +blockWoolGreen +blockWoolRed +blockWoolBlack +oreCopper +oreTin +oreSilver +oreLead +oreAluminum +oreNickel +orePlatinum +oreIridium +oreMithril +oreClathrateOilSand +oreClathrateOilShale +oreClathrateRedstone +oreClathrateGlowstone +oreClathrateEnder +blockCopper +blockTin +blockSilver +blockLead +blockAluminum +blockNickel +blockPlatinum +blockIridium +blockMithril +blockSteel +blockElectrum +blockInvar +blockBronze +blockConstantan +blockSignalum +blockLumium +blockEnderium +blockCharcoal +blockFuelCoke +blockGlassHardened +blockRockwool +coinIron +coinGold +coinCopper +coinTin +coinSilver +coinLead +coinAluminum +coinNickel +coinPlatinum +coinIridium +coinMithril +coinSteel +coinElectrum +coinInvar +coinBronze +coinConstantan +coinSignalum +coinLumium +coinEnderium +dustIron +dustGold +nuggetDiamond +nuggetEmerald +gearWood +gearStone +gearIron +gearGold +gearDiamond +gearEmerald +plateIron +plateGold +dustCopper +dustTin +dustSilver +dustLead +dustAluminum +dustNickel +dustPlatinum +dustIridium +dustMithril +dustSteel +dustElectrum +dustInvar +dustBronze +dustConstantan +dustSignalum +dustLumium +dustEnderium +ingotCopper +ingotTin +ingotSilver +ingotLead +ingotAluminum +ingotNickel +ingotPlatinum +ingotIridium +ingotMithril +ingotSteel +ingotElectrum +ingotInvar +ingotBronze +ingotConstantan +ingotSignalum +ingotLumium +ingotEnderium +nuggetCopper +nuggetTin +nuggetSilver +nuggetLead +nuggetAluminum +nuggetNickel +nuggetPlatinum +nuggetIridium +nuggetMithril +nuggetSteel +nuggetElectrum +nuggetInvar +nuggetBronze +nuggetConstantan +nuggetSignalum +nuggetLumium +nuggetEnderium +gearCopper +gearTin +gearSilver +gearLead +gearAluminum +gearNickel +gearPlatinum +gearIridium +gearMithril +gearSteel +gearElectrum +gearInvar +gearBronze +gearConstantan +gearSignalum +gearLumium +gearEnderium +plateCopper +plateTin +plateSilver +plateLead +plateAluminum +plateNickel +platePlatinum +plateIridium +plateMithril +plateSteel +plateElectrum +plateInvar +plateBronze +plateConstantan +plateSignalum +plateLumium +plateEnderium +dustCoal +dustCharcoal +dustObsidian +dustSulfur +dustWood +fuelCoke +crystalSlag +crystalSlagRich +crystalCinnabar +crystalCrudeOil +crystalRedstone +crystalGlowstone +crystalEnder +dustPyrotheum +dustCryotheum +dustAerotheum +dustPetrotheum +dustMana +rodBlizz +dustBlizz +rodBlitz +dustBlitz +rodBasalz +dustBasalz +dustSaltpeter +itemSawdust +itemBiomass +itemBioblend +itemBiomassRich +itemBioblendRich +itemSlag +itemSlagRich +itemCinnabar +clathrateOil +clathrateRedstone +clathrateGlowstone +clathrateEnder +dragonEgg +oreDraconium +blockDraconium +blockDraconiumAwakened +ingotDraconium +dustDraconium +ingotDraconiumAwakened +nuggetDraconium +nuggetDraconiumAwakened +blockCrystalMatrix +blockCosmicNeutronium +blockInfinity +ingotCrystalMatrix +ingotCosmicNeutronium +ingotInfinity +nuggetCosmicNeutronium + +ingotUnstable +nuggetUnstable +compressed1xCobblestone +compressed2xCobblestone +compressed3xCobblestone +compressed4xCobblestone +compressed5xCobblestone +compressed6xCobblestone +compressed7xCobblestone +compressed8xCobblestone +compressed1xDirt +compressed2xDirt +compressed3xDirt +compressed4xDirt +compressed1xSand +compressed2xSand +compressed1xGravel +compressed2xGravel +compressed1xNetherrack +compressed2xNetherrack +compressed3xNetherrack +compressed4xNetherrack +compressed5xNetherrack +compressed6xNetherrack +gemRedstone +gearRedstone +eyeofredstone +dustLunar +coalPowered +gemMoon +xuUpgradeSpeed +xuUpgradeStack +xuUpgradeMining +xuUpgradeBlank +dropofevil +ingotDemonicMetal +ingotEnchantedMetal +xuRedstoneCoil +xuUpgradeSpeedEnchanted +ingotEvilMetal +blockEnchantedMetal +blockDemonicMetal +blockEvilMetal +blockMagicalWood +bookshelf +bricksStone +rodStone +projredIllumar +gemRuby +gemSapphire +gemPeridot +ingotRedAlloy +ingotElectrotineAlloy +dustElectrotine +gearDiamatine +gearEmeradic +gearEnori +gearPalis +gearRestonia +gearVoid +gearDiamatineEmpowered +gearEmeradicEmpowered +gearEnoriEmpowered +gearPalisEmpowered +gearRestoniaEmpowered +gearVoidEmpowered +plateFluix +plateInfinity +plateDemonicMetal +blockGraphite +blockBeryllium +blockZirconium +blockManganese +blockFissionModerator +blockThorium230 +blockUranium238 +blockNeptunium237 +blockPlutonium242 +blockAmericium243 +blockCurium246 +blockBerkelium247 +blockCalifornium252 +ingotGraphite +ingotBeryllium +ingotZirconium +ingotManganese +ingotThoriumOxide +ingotUraniumOxide +ingotManganeseOxide +ingotManganeseDioxide +dustGraphite +dustBeryllium +dustZirconium +dustManganese +dustThoriumOxide +dustUraniumOxide +dustManganeseOxide +dustManganeseDioxide +gemRhodochrosite +gemBoronNitride +gemFluorite +gemVilliaumite +gemCarobbiite +gemBoronArsenide +itemSilicon +dustDiamond +dustRhodochrosite +dustQuartz +dustNetherQuartz +dustBoronNitride +dustFluorite +dustVilliaumite +dustCarobbiite +dustArsenic +dustEndstone +ingotTough +ingotHardCarbon +ingotMagnesiumDiboride +ingotLithiumManganeseDioxide +ingotFerroboron +ingotShibuichi +ingotTinSilver +ingotLeadPlatinum +ingotExtreme +ingotThermoconducting +ingotZircaloy +ingotSiliconCarbide +ingotSiCSiCCMC +ingotHSLASteel +dustCalciumSulfate +dustCrystalBinder +dustEnergetic +dustSodiumFluoride +dustPotassiumFluoride +dustSodiumHydroxide +dustPotassiumHydroxide +dustBorax +dustDimensional +dustCarbonManganese +dustAlugentum +plateBasic +plateAdvanced +plateDU +plateElite +solenoidCopper +solenoidMagnesiumDiboride +bioplastic +servo +motor +actuator +chassis +emptyFrame +steelFrame +fiberSiliconCarbide +tinyDustLead +ingotThorium230Base +ingotThorium230 +ingotThorium230Oxide +nuggetThorium230 +nuggetThorium230Oxide +ingotThorium232Base +ingotThorium232 +ingotThorium232Oxide +nuggetThorium232 +nuggetThorium232Oxide +ingotUranium233Base +ingotUranium233 +ingotUranium233Oxide +nuggetUranium233 +nuggetUranium233Oxide +ingotUranium235Base +ingotUranium235 +ingotUranium235Oxide +nuggetUranium235 +nuggetUranium235Oxide +ingotUranium238Base +ingotUranium238 +ingotUranium238Oxide +nuggetUranium238 +nuggetUranium238Oxide +ingotNeptunium236Base +ingotNeptunium236 +ingotNeptunium236Oxide +nuggetNeptunium236 +nuggetNeptunium236Oxide +ingotNeptunium237Base +ingotNeptunium237 +ingotNeptunium237Oxide +nuggetNeptunium237 +nuggetNeptunium237Oxide +ingotPlutonium238Base +ingotPlutonium238 +ingotPlutonium238Oxide +nuggetPlutonium238 +nuggetPlutonium238Oxide +ingotPlutonium239Base +ingotPlutonium239 +ingotPlutonium239Oxide +nuggetPlutonium239 +nuggetPlutonium239Oxide +ingotPlutonium241Base +ingotPlutonium241 +ingotPlutonium241Oxide +nuggetPlutonium241 +nuggetPlutonium241Oxide +ingotPlutonium242Base +ingotPlutonium242 +ingotPlutonium242Oxide +nuggetPlutonium242 +nuggetPlutonium242Oxide +ingotAmericium241Base +ingotAmericium241 +ingotAmericium241Oxide +nuggetAmericium241 +nuggetAmericium241Oxide +ingotAmericium242Base +ingotAmericium242 +ingotAmericium242Oxide +nuggetAmericium242 +nuggetAmericium242Oxide +ingotAmericium243Base +ingotAmericium243 +ingotAmericium243Oxide +nuggetAmericium243 +nuggetAmericium243Oxide +ingotCurium243Base +ingotCurium243 +ingotCurium243Oxide +nuggetCurium243 +nuggetCurium243Oxide +ingotCurium245Base +ingotCurium245 +ingotCurium245Oxide +nuggetCurium245 +nuggetCurium245Oxide +ingotCurium246Base +ingotCurium246 +ingotCurium246Oxide +nuggetCurium246 +nuggetCurium246Oxide +ingotCurium247Base +ingotCurium247 +ingotCurium247Oxide +nuggetCurium247 +nuggetCurium247Oxide +ingotBerkelium247Base +ingotBerkelium247 +ingotBerkelium247Oxide +nuggetBerkelium247 +nuggetBerkelium247Oxide +ingotBerkelium248Base +ingotBerkelium248 +ingotBerkelium248Oxide +nuggetBerkelium248 +nuggetBerkelium248Oxide +ingotCalifornium249Base +ingotCalifornium249 +ingotCalifornium249Oxide +nuggetCalifornium249 +nuggetCalifornium249Oxide +ingotCalifornium250Base +ingotCalifornium250 +ingotCalifornium250Oxide +nuggetCalifornium250 +nuggetCalifornium250Oxide +ingotCalifornium251Base +ingotCalifornium251 +ingotCalifornium251Oxide +nuggetCalifornium251 +nuggetCalifornium251Oxide +ingotCalifornium252Base +ingotCalifornium252 +ingotCalifornium252Oxide +nuggetCalifornium252 +nuggetCalifornium252Oxide +fuelTBU +fuelTBUOxide +fuelLEU233 +fuelLEU233Oxide +fuelHEU233 +fuelHEU233Oxide +fuelLEU235 +fuelLEU235Oxide +fuelHEU235 +fuelHEU235Oxide +fuelLEN236 +fuelLEN236Oxide +fuelHEN236 +fuelHEN236Oxide +fuelLEP239 +fuelLEP239Oxide +fuelHEP239 +fuelHEP239Oxide +fuelLEP241 +fuelLEP241Oxide +fuelHEP241 +fuelHEP241Oxide +fuelMOX239 +fuelMOX241 +fuelLEA242 +fuelLEA242Oxide +fuelHEA242 +fuelHEA242Oxide +fuelLECm243 +fuelLECm243Oxide +fuelHECm243 +fuelHECm243Oxide +fuelLECm245 +fuelLECm245Oxide +fuelHECm245 +fuelHECm245Oxide +fuelLECm247 +fuelLECm247Oxide +fuelHECm247 +fuelHECm247Oxide +fuelLEB248 +fuelLEB248Oxide +fuelHEB248 +fuelHEB248Oxide +fuelLECf249 +fuelLECf249Oxide +fuelHECf249 +fuelHECf249Oxide +fuelLECf251 +fuelLECf251Oxide +fuelHECf251 +fuelHECf251Oxide +depletedFuelTBU +depletedFuelTBUOxide +depletedFuelLEU233 +depletedFuelLEU233Oxide +depletedFuelHEU233 +depletedFuelHEU233Oxide +depletedFuelLEU235 +depletedFuelLEU235Oxide +depletedFuelHEU235 +depletedFuelHEU235Oxide +depletedFuelLEN236 +depletedFuelLEN236Oxide +depletedFuelHEN236 +depletedFuelHEN236Oxide +depletedFuelLEP239 +depletedFuelLEP239Oxide +depletedFuelHEP239 +depletedFuelHEP239Oxide +depletedFuelLEP241 +depletedFuelLEP241Oxide +depletedFuelHEP241 +depletedFuelHEP241Oxide +depletedFuelMOX239 +depletedFuelMOX241 +depletedFuelLEA242 +depletedFuelLEA242Oxide +depletedFuelHEA242 +depletedFuelHEA242Oxide +depletedFuelLECm243 +depletedFuelLECm243Oxide +depletedFuelHECm243 +depletedFuelHECm243Oxide +depletedFuelLECm245 +depletedFuelLECm245Oxide +depletedFuelHECm245 +depletedFuelHECm245Oxide +depletedFuelLECm247 +depletedFuelLECm247Oxide +depletedFuelHECm247 +depletedFuelHECm247Oxide +depletedFuelLEB248 +depletedFuelLEB248Oxide +depletedFuelHEB248 +depletedFuelHEB248Oxide +depletedFuelLECf249 +depletedFuelLECf249Oxide +depletedFuelHECf249 +depletedFuelHECf249Oxide +depletedFuelLECf251 +depletedFuelLECf251Oxide +depletedFuelHECf251 +depletedFuelHECf251Oxide +depletedFuelIC2U +depletedFuelIC2MOX +ingotBoron10 +nuggetBoron10 +ingotBoron11 +nuggetBoron11 +ingotLithium6 +nuggetLithium6 +ingotLithium7 +nuggetLithium7 +gearDominos +dustWheat +dustCocoa +ingotCocoaButter +ingotUnsweetenedChocolate +ingotDarkChocolate +ingotChocolate +ingotMarshmallow +projredInsFramedWire +projredInsulatedWire +projredBundledCable +blockElectrumFlux +blockCrystalFlux +dustElectrumFlux +ingotElectrumFlux +nuggetElectrumFlux +gearElectrumFlux +plateElectrumFlux +gemCrystalFlux +dustDilithium +gemDilithium +oreDilithium +stickIron +sheetIron +coilGold +blockCoil +dustSilicon +ingotSilicon +bouleSilicon +nuggetSilicon +plateSilicon +stickCopper +sheetCopper +coilCopper +stickSteel +fanSteel +sheetSteel +dustTitanium +ingotTitanium +nuggetTitanium +plateTitanium +stickTitanium +sheetTitanium +gearTitanium +blockTitanium +coilTitanium +oreRutile +oreTitanium +sheetAluminum +coilAluminum +stickIridium +coilIridium +blockMotor +dustTitaniumAluminide +ingotTitaniumAluminide +nuggetTitaniumAluminide +plateTitaniumAluminide +stickTitaniumAluminide +sheetTitaniumAluminide +gearTitaniumAluminide +blockTitaniumAluminide +dustTitaniumIridium +ingotTitaniumIridium +nuggetTitaniumIridium +plateTitaniumIridium +stickTitaniumIridium +sheetTitaniumIridium +gearTitaniumIridium +blockTitaniumIridium +turfMoon +concrete +stoneBasalt +stoneBasaltPolished +bookshelfOak +bookshelfSpruce +bookshelfBirch +bookshelfJungle +bookshelfAcacia +bookshelfDarkOak +blockCobalt +blockCoalCoke +blockMossy +blockConcrete +blockConcreteBlack +blockConcreteRed +blockConcreteGreen +blockConcreteBrown +blockConcreteBlue +blockConcretePurple +blockConcreteCyan +blockConcreteLightGray +blockConcreteGray +blockConcretePink +blockConcreteLime +blockConcreteYellow +blockConcreteLightBlue +blockConcreteMagenta +blockConcreteOrange +blockConcreteWhite +hardenedClay +ice +blockIce +stoneLimestone +stoneLimestonePolished +stoneMarble +stoneMarblePolished +prismarine +prismarineBrick +prismarineDark +brickStone +blockUranium +itemBattery +waferSilicon +ingotCarbon +itemLens +dustThermite +slab +blockWarpCoreCore +blockWarpCoreRim +itemQuartzWrench +itemQuartzKnife +crystalCertusQuartz +dustCertusQuartz +crystalFluix +dustFluix +pearlFluix +crystalPureCertusQuartz +crystalPureNetherQuartz +crystalPureFluix +dustEnder +dustEnderPearl +itemIlluminatedPanel +blockElevator +blockBlackIron +blockLuminessence +blockNetherStar +blockCrystaltine +blockUltimate +ingotBlackIron +ingotUltimate +nuggetNetherStar +nuggetUltimate +nuggetCrystaltine +nuggetBlackIron +ingotCrystaltine +drawerBasic +drawerTrim +circuitUlv +componentTransistor +componentResistor +componentCapacitor +componentDiode +componentInductor +circuitLv +circuitMv +circuitHv +circuitEv +circuitIv +circuitLuv +circuitZpm +circuitUv +circuitUhv +batteryUlv +batteryLv +batteryMv +batteryHv +batteryEv +batteryIv +batteryLuv +batteryZpm +batteryUv +batteryUhv +dustEmerald +dustLapis +dustQuartzBlack +oreQuartzBlack +gemQuartzBlack +seedCanola +cropCanola +seedRice +cropRice +seedFlax +cropFlax +seedCoffee +cropCoffee +blockQuartzBlack +oreArdite +oreCobalt +itemSimpleMachineChassi +itemMachineChassi +itemChassiParts +itemPlatePhotovoltaic +itemConduitBinder +itemGliderWing +itemGliderWings +itemNutritiousStick +gearIronInfinity +gearEnergized +gearVibrant +itemPulsatingCrystal +itemVibrantCrystal +itemEnderCrystal +itemAttractorCrystal +itemWeatherCrystal +itemPrecientCrystal +dustBedrock +itemBinderComposite +nuggetEnderpearl +itemPrecientPowder +itemVibrantPowder +itemPulsatingPowder +itemEnderCrystalPowder +itemPowderPhotovoltaic +skullZombieElectrode +skullZombieController +skullZombieFrankenstein +skullEnderResonator +skullSentientEnder +skullSkeletalContractor +itemPlantgreen +itemPlantbrown +dyeMachine +dyeSoulMachine +itemSoulMachineChassi +itemEnhancedMachineChassi +itemUnsouledMachineChassi +skullGuardianDiode +itemGrindingBallSignalum +itemGrindingBallEnderium +itemGrindingBallLumium +itemRedstoneFilterBase +itemConfusingDust +itemEnderFragment +itemWitheringDust +itemRemoteAwarenessUpgrade +itemWirelessDish +itemEndSteelMachineChassi +dyeEnhancedMachine +itemEnhancedChassiParts +itemSimpleChassiParts +itemCakeBase +itemInfinityRod +ingotBrickNetherGlazed +gearDark +dustSoularium +itemInfinityGoop +itemClayedGlowstone +paperBlack +itemTokenAnimal +itemTokenMonster +itemTokenPlayer +itemDeathUrnUnfired +blockElectricalSteel +ingotElectricalSteel +nuggetElectricalSteel +ballElectricalSteel +blockEnergeticAlloy +ingotEnergeticAlloy +nuggetEnergeticAlloy +ballEnergeticAlloy +blockVibrantAlloy +ingotVibrantAlloy +nuggetVibrantAlloy +ballVibrantAlloy +blockRedstoneAlloy +ingotRedstoneAlloy +nuggetRedstoneAlloy +ballRedstoneAlloy +blockConductiveIron +ingotConductiveIron +nuggetConductiveIron +ballConductiveIron +blockPulsatingIron +ingotPulsatingIron +nuggetPulsatingIron +ballPulsatingIron +blockDarkSteel +ingotDarkSteel +nuggetDarkSteel +ballDarkSteel +blockSoularium +ingotSoularium +nuggetSoularium +ballSoularium +blockEndSteel +ingotEndSteel +nuggetEndSteel +ballEndSteel +blockConstructionAlloy +ingotConstructionAlloy +nuggetConstructionAlloy +ballConstructionAlloy +blockCrudeSteel +ingotCrudeSteel +nuggetCrudeSteel +ballCrudeSteel +blockCrystallineAlloy +ingotCrystallineAlloy +nuggetCrystallineAlloy +ballCrystallineAlloy +blockMelodicAlloy +ingotMelodicAlloy +nuggetMelodicAlloy +ballMelodicAlloy +blockStellarAlloy +ingotStellarAlloy +nuggetStellarAlloy +ballStellarAlloy +blockCrystallinePinkSlime +ingotCrystallinePinkSlime +nuggetCrystallinePinkSlime +ballCrystallinePinkSlime +blockEnergeticSilver +ingotEnergeticSilver +nuggetEnergeticSilver +ballEnergeticSilver +blockVividAlloy +ingotVividAlloy +nuggetVividAlloy +ballVividAlloy +blockGlassHardenedBlack +blockGlassHardenedRed +blockGlassHardenedGreen +blockGlassHardenedBrown +blockGlassHardenedBlue +blockGlassHardenedPurple +blockGlassHardenedCyan +blockGlassHardenedLightGray +blockGlassHardenedGray +blockGlassHardenedPink +blockGlassHardenedLime +blockGlassHardenedYellow +blockGlassHardenedLightBlue +blockGlassHardenedMagenta +blockGlassHardenedOrange +blockGlassHardenedWhite +fusedQuartz +fusedGlass +enlightenedFusedQuartz +enlightenedFusedGlass +darkFusedQuartz +darkFusedGlass +holyFusedQuartz +holyFusedGlass +holyEnlightenedFusedQuartz +holyEnlightenedFusedGlass +holyDarkFusedQuartz +holyDarkFusedGlass +unholyFusedQuartz +unholyFusedGlass +unholyEnlightenedFusedQuartz +unholyEnlightenedFusedGlass +unholyDarkFusedQuartz +unholyDarkFusedGlass +pastureFusedQuartz +pastureFusedGlass +pastureEnlightenedFusedQuartz +pastureEnlightenedFusedGlass +pastureDarkFusedQuartz +pastureDarkFusedGlass +notHolyFusedQuartz +notHolyFusedGlass +notHolyEnlightenedFusedQuartz +notHolyEnlightenedFusedGlass +notHolyDarkFusedQuartz +notHolyDarkFusedGlass +notUnholyFusedQuartz +notUnholyFusedGlass +notUnholyEnlightenedFusedQuartz +notUnholyEnlightenedFusedGlass +notUnholyDarkFusedQuartz +notUnholyDarkFusedGlass +notPastureFusedQuartz +notPastureFusedGlass +notPastureEnlightenedFusedQuartz +notPastureEnlightenedFusedGlass +notPastureDarkFusedQuartz +notPastureDarkFusedGlass +itemSkull +toolHoe +eggOwl +toolShears +toolTreetap +compressed1xDustBedrock +compressed2xDustBedrock +compressed3xDustBedrock +rodIron +rodTitanium +rodSteel +rodTitaniumAluminide +rodCopper +ingotBrass +ingotAluminium +glass +ingotElvenElementium +ingotGaia +ingotManasteel +ingotTerrasteel +gemAmethyst +ingotEnrichedGold +gemTanzanite +ingotOsmium +ingotRefinedGlowstone +ingotRefinedObsidian +ingotInsanium +ingotInferium +ingotIntermedium +ingotPrudentium +ingotSoulium +ingotSuperium +ingotSupremium +ingotAlumite +ingotMirion +ingotOsgloglas +ingotOsmiridium +ingotIronCompressed +ingotCorrupted +gemAmber +quicksilver +ingotArdite +ingotCobalt +ingotKnightslime +ingotManyullyn +ingotPigiron +ingotFiery +ingotIronwood +ingotKnightmetal +gemPearl +blockPearl +bed +dustAwakenedDraconium +dustDarkSteel +dustOmnium +dustArdite +dustManyullyn +dustConductiveIron +dustEnergeticAlloy +dustVibrantAlloy +dustPulsatingIron +dustElectricalSteel +dustCrystalMatrix +dustEndSteel +dustInfinity +dustMicroversium +dustDraconicSuperconductor +dustOsmiridium8020 +dustIridosmine8020 +dustKaemanite +dustTungstenTrioxide +dustBerylliumOxide +dustNiobiumPentoxide +dustTantalumPentoxide +dustManganeseDifluoride +dustMolybdenumTrioxide +dustLeadChloride +dustWollastonite +dustSodiumMetavanadate +dustVanadiumPentoxide +dustAmmoniumMetavanadate +dustPhthalicAnhydride +dustEthylanthraquinone +dustGrapheneOxide +dustKaptonK +dustDurene +dustPyromelliticDianhydride +dustOxydianiline +dustNaquadahOxide +dustPyromorphite +dustNaquadahHydroxide +dustSnowchestite +dustCaesiumHydroxide +dustLeadMetasilicate +dustPlatinumMetallic +dustPalladiumMetallic +dustAmmoniumHexachloroplatinate +dustPotassiumBisulfate +dustPotassiumPyrosulfate +dustPotassiumSulfate +dustZincSulfate +dustSodiumNitrate +dustRhodiumNitrate +dustSodiumRuthenate +dustSodiumPeroxide +dustIridiumDioxideResidue +dustAmmoniumHexachloroiridiate +dustPlatinumGroupResidue +dustCrudePlatinumResidue +dustCrudePalladiumResidue +dustIridiumGroupSludge +dustCrudeRhodiumResidue +dustRhodiumSalt +dustSodiumMethoxide +dustStoneResidue +dustUncommonResidue +dustOxidisedResidue +dustRefinedResidue +dustCleanInertResidue +dustTaranium +dustDarmstadtite +dustDulysite +dustLaurite +dustCuprorhodsite +dustAluminium +dustAmericium +dustAntimony +dustBarium +dustBerkelium +dustBismuth +dustBoron +dustCaesium +dustCalcium +dustCalifornium +dustCarbon +dustCadmium +dustCerium +dustChrome +dustCobalt +dustCurium +dustDarmstadtium +dustEinsteinium +dustEuropium +dustGallium +dustIndium +dustLanthanum +dustLithium +dustLutetium +dustMagnesium +dustMolybdenum +dustNeodymium +dustNeptunium +dustNiobium +dustOsmium +dustPalladium +dustPhosphorus +dustPlutonium +dustPlutonium239 +dustPlutonium241 +dustPotassium +dustRhodium +dustRuthenium +dustSamarium +dustSodium +dustTantalum +dustThorium +dustTungsten +dustUranium +dustUranium238 +dustUranium235 +dustVanadium +dustYttrium +dustZinc +dustNaquadah +dustNaquadahEnriched +dustNaquadria +dustNeutronium +dustTritanium +dustDuranium +dustTrinium +dustAlmandine +dustAndradite +dustAnnealedCopper +dustAsbestos +dustAsh +dustBandedIron +dustBatteryAlloy +dustBlueTopaz +dustBrass +dustBrownLimonite +dustCalcite +dustCassiterite +dustCassiteriteSand +dustChalcopyrite +dustChromite +dustCinnabar +dustCobaltite +dustCooperite +dustCupronickel +dustDarkAsh +dustGalena +dustGarnierite +dustGreenSapphire +dustGrossular +dustIce +dustIlmenite +dustRutile +dustBauxite +dustKanthal +dustLazurite +dustMagnalium +dustMagnesite +dustMagnetite +dustMolybdenite +dustNichrome +dustNiobiumNitride +dustNiobiumTitanium +dustPhosphate +dustPlatinumRaw +dustSterlingSilver +dustRoseGold +dustBlackBronze +dustBismuthBronze +dustBiotite +dustPowellite +dustPyrite +dustPyrolusite +dustPyrope +dustRockSalt +dustRuridit +dustRuby +dustSalt +dustSapphire +dustScheelite +dustSodalite +dustAluminiumSulfite +dustTantalite +dustCoke +dustSolderingAlloy +dustSpessartine +dustSphalerite +dustStainlessSteel +dustStibnite +dustTetrahedrite +dustTinAlloy +dustTopaz +dustTungstate +dustUltimet +dustUraninite +dustUvarovite +dustVanadiumGallium +dustWroughtIron +dustWulfenite +dustYellowLimonite +dustYttriumBariumCuprate +dustQuartzite +dustGraphene +dustTungsticAcid +dustOsmiridium +dustLithiumChloride +dustCalciumChloride +dustBornite +dustChalcocite +dustGalliumArsenide +dustPotash +dustSodaAsh +dustIndiumGalliumPhosphide +dustNickelZincFerrite +dustSiliconDioxide +dustMagnesiumChloride +dustSodiumSulfide +dustPhosphorusPentoxide +dustQuicklime +dustSodiumBisulfate +dustFerriteMixture +dustMagnesia +dustPlatinumGroupSludge +dustRealgar +dustSodiumBicarbonate +dustPotassiumDichromate +dustChromiumTrioxide +dustAntimonyTrioxide +dustZincite +dustCupricOxide +dustCobaltOxide +dustArsenicTrioxide +dustMassicot +dustFerrosilite +dustMetalMixture +dustBastnasite +dustPentlandite +dustSpodumene +dustLepidolite +dustGlauconiteSand +dustMalachite +dustMica +dustBarite +dustAlunite +dustTalc +dustSoapstone +dustKyanite +dustIronMagnetic +dustTungstenCarbide +dustPotassiumFeldspar +dustNeodymiumMagnetic +dustSamariumMagnetic +dustManganesePhosphide +dustMagnesiumDiboride +dustMercuryBariumCalciumCuprate +dustUraniumTriplatinum +dustSamariumIronArsenicOxide +dustIndiumTinBariumTitaniumCuprate +dustUraniumRhodiumDinaquadide +dustEnrichedNaquadahTriniumEuropiumDuranide +dustRutheniumTriniumAmericiumNeutronate +dustInertMetalMixture +dustRhodiumSulfate +dustRutheniumTetroxide +dustOsmiumTetroxide +dustIridiumChloride +dustTitaniumTrifluoride +dustCalciumPhosphide +dustIndiumPhosphide +dustBariumSulfide +dustTriniumSulfide +dustZincSulfide +dustGalliumSulfide +dustAntimonyTrifluoride +dustEnrichedNaquadahSulfate +dustNaquadriaSulfate +dustPyrochlore +dustRtmAlloy +dustSiliconeRubber +dustRawRubber +dustRawStyreneButadieneRubber +dustStyreneButadieneRubber +dustReinforcedEpoxyResin +dustPolyvinylChloride +dustPolyphenyleneSulfide +dustPolybenzimidazole +dustPolydimethylsiloxane +dustPlastic +dustEpoxy +dustPolycaprolactam +dustPolytetrafluoroethylene +dustRubber +dustCyclohexanoneOxime +dustCaprolactam +dustPolyvinylButyral +dustBiphenyl +dustOilsands +dustRareEarth +dustStone +dustNetherStar +dustNetherrack +dustCollagen +dustGelatin +dustAgar +dustMeat +dustPaper +dustLapotron +dustTreatedWood +dustGlass +dustPerlite +dustOlivine +dustOpal +dustAmethyst +dustApatite +dustBlackSteel +dustDamascusSteel +dustTungstenSteel +dustCobaltBrass +dustTricalciumPhosphate +dustGarnetRed +dustGarnetYellow +dustMarble +dustGraniteBlack +dustGraniteRed +dustVanadiumMagnetite +dustQuartzSand +dustPollucite +dustBentonite +dustFullersEarth +dustPitchblende +dustMonazite +dustMirabilite +dustTrona +dustGypsum +dustZeolite +dustConcrete +dustSteelMagnetic +dustVanadiumSteel +dustPotin +dustBorosilicateGlass +dustAndesite +dustNaquadahAlloy +dustFlint +dustPlatinumSludgeResidue +dustPalladiumRaw +dustRarestMetalMixture +dustAmmoniumChloride +dustRhodiumPlatedPalladium +dustClay +dustEnderEye +dustDiatomite +dustRedSteel +dustBlueSteel +dustBasalt +dustGraniticMineralSand +dustRedrock +dustGarnetSand +dustHssg +dustRedAlloy +dustBasalticMineralSand +dustHsse +dustHsss +dustIridiumMetalResidue +dustGranite +dustBrick +dustFireclay +dustDiorite +dustBlueAlloy +dustStellite100 +dustWatertightSteel +dustMaragingSteel300 +dustHastelloyC276 +dustHastelloyX +dustTrinaquadalloy +dustZeron100 +dustTitaniumCarbide +dustTantalumCarbide +dustMolybdenumDisilicide +dustHslaSteel +dustTitaniumTungstenCarbide +dustIncoloyMa956 +dustSmallDraconium +dustSmallAwakenedDraconium +dustSmallDarkSteel +dustSmallOmnium +dustSmallArdite +dustSmallManyullyn +dustSmallSignalum +dustSmallConductiveIron +dustSmallEnergeticAlloy +dustSmallVibrantAlloy +dustSmallPulsatingIron +dustSmallElectricalSteel +dustSmallLumium +dustSmallEnderium +dustSmallElectrumFlux +dustSmallMithril +dustSmallCrystalMatrix +dustSmallSoularium +dustSmallEndSteel +dustSmallInfinity +dustSmallMicroversium +dustSmallDraconicSuperconductor +dustSmallOsmiridium8020 +dustSmallIridosmine8020 +dustSmallKaemanite +dustSmallTungstenTrioxide +dustSmallBerylliumOxide +dustSmallNiobiumPentoxide +dustSmallTantalumPentoxide +dustSmallFluorite +dustSmallManganeseDifluoride +dustSmallMolybdenumTrioxide +dustSmallLeadChloride +dustSmallWollastonite +dustSmallSodiumMetavanadate +dustSmallVanadiumPentoxide +dustSmallAmmoniumMetavanadate +dustSmallPhthalicAnhydride +dustSmallEthylanthraquinone +dustSmallGrapheneOxide +dustSmallKaptonK +dustSmallDurene +dustSmallPyromelliticDianhydride +dustSmallOxydianiline +dustSmallNaquadahOxide +dustSmallPyromorphite +dustSmallNaquadahHydroxide +dustSmallSnowchestite +dustSmallCaesiumHydroxide +dustSmallLeadMetasilicate +dustSmallPlatinumMetallic +dustSmallPalladiumMetallic +dustSmallAmmoniumHexachloroplatinate +dustSmallPotassiumBisulfate +dustSmallPotassiumPyrosulfate +dustSmallPotassiumSulfate +dustSmallZincSulfate +dustSmallSodiumNitrate +dustSmallRhodiumNitrate +dustSmallSodiumRuthenate +dustSmallSodiumPeroxide +dustSmallIridiumDioxideResidue +dustSmallAmmoniumHexachloroiridiate +dustSmallPlatinumGroupResidue +dustSmallCrudePlatinumResidue +dustSmallCrudePalladiumResidue +dustSmallIridiumGroupSludge +dustSmallCrudeRhodiumResidue +dustSmallRhodiumSalt +dustSmallSodiumMethoxide +dustSmallStoneResidue +dustSmallUncommonResidue +dustSmallOxidisedResidue +dustSmallRefinedResidue +dustSmallCleanInertResidue +dustSmallTaranium +dustSmallDarmstadtite +dustSmallDulysite +dustSmallLaurite +dustSmallCuprorhodsite +dustSmallAluminium +dustSmallAmericium +dustSmallAntimony +dustSmallArsenic +dustSmallBarium +dustSmallBerkelium +dustSmallBeryllium +dustSmallBismuth +dustSmallBoron +dustSmallCaesium +dustSmallCalcium +dustSmallCalifornium +dustSmallCarbon +dustSmallCadmium +dustSmallCerium +dustSmallChrome +dustSmallCobalt +dustSmallCopper +dustSmallCurium +dustSmallDarmstadtium +dustSmallEinsteinium +dustSmallEuropium +dustSmallGallium +dustSmallGold +dustSmallIndium +dustSmallIridium +dustSmallIron +dustSmallLanthanum +dustSmallLead +dustSmallLithium +dustSmallLutetium +dustSmallMagnesium +dustSmallManganese +dustSmallMolybdenum +dustSmallNeodymium +dustSmallNeptunium +dustSmallNickel +dustSmallNiobium +dustSmallOsmium +dustSmallPalladium +dustSmallPhosphorus +dustSmallPlatinum +dustSmallPlutonium +dustSmallPlutonium239 +dustSmallPlutonium241 +dustSmallPotassium +dustSmallRhodium +dustSmallRuthenium +dustSmallSamarium +dustSmallSilicon +dustSmallSilver +dustSmallSodium +dustSmallSulfur +dustSmallTantalum +dustSmallThorium +dustSmallTin +dustSmallTitanium +dustSmallTungsten +dustSmallUranium +dustSmallUranium238 +dustSmallUranium235 +dustSmallVanadium +dustSmallYttrium +dustSmallZinc +dustSmallNaquadah +dustSmallNaquadahEnriched +dustSmallNaquadria +dustSmallNeutronium +dustSmallTritanium +dustSmallDuranium +dustSmallTrinium +dustSmallCertusQuartz +dustSmallAlmandine +dustSmallAndradite +dustSmallAnnealedCopper +dustSmallAsbestos +dustSmallAsh +dustSmallBandedIron +dustSmallBatteryAlloy +dustSmallBlueTopaz +dustSmallBone +dustSmallBrass +dustSmallBronze +dustSmallBrownLimonite +dustSmallCalcite +dustSmallCassiterite +dustSmallCassiteriteSand +dustSmallChalcopyrite +dustSmallCharcoal +dustSmallChromite +dustSmallCinnabar +dustSmallCoal +dustSmallCobaltite +dustSmallCooperite +dustSmallCupronickel +dustSmallDarkAsh +dustSmallDiamond +dustSmallElectrum +dustSmallEmerald +dustSmallGalena +dustSmallGarnierite +dustSmallGreenSapphire +dustSmallGrossular +dustSmallIce +dustSmallIlmenite +dustSmallRutile +dustSmallBauxite +dustSmallInvar +dustSmallKanthal +dustSmallLazurite +dustSmallMagnalium +dustSmallMagnesite +dustSmallMagnetite +dustSmallMolybdenite +dustSmallNichrome +dustSmallNiobiumNitride +dustSmallNiobiumTitanium +dustSmallObsidian +dustSmallPhosphate +dustSmallPlatinumRaw +dustSmallSterlingSilver +dustSmallRoseGold +dustSmallBlackBronze +dustSmallBismuthBronze +dustSmallBiotite +dustSmallPowellite +dustSmallPyrite +dustSmallPyrolusite +dustSmallPyrope +dustSmallRockSalt +dustSmallRuridit +dustSmallRuby +dustSmallSalt +dustSmallSaltpeter +dustSmallSapphire +dustSmallScheelite +dustSmallSodalite +dustSmallAluminiumSulfite +dustSmallTantalite +dustSmallCoke +dustSmallSolderingAlloy +dustSmallSpessartine +dustSmallSphalerite +dustSmallStainlessSteel +dustSmallSteel +dustSmallStibnite +dustSmallTetrahedrite +dustSmallTinAlloy +dustSmallTopaz +dustSmallTungstate +dustSmallUltimet +dustSmallUraninite +dustSmallUvarovite +dustSmallVanadiumGallium +dustSmallWroughtIron +dustSmallWulfenite +dustSmallYellowLimonite +dustSmallYttriumBariumCuprate +dustSmallNetherQuartz +dustSmallQuartzite +dustSmallGraphite +dustSmallGraphene +dustSmallTungsticAcid +dustSmallOsmiridium +dustSmallLithiumChloride +dustSmallCalciumChloride +dustSmallBornite +dustSmallChalcocite +dustSmallGalliumArsenide +dustSmallPotash +dustSmallSodaAsh +dustSmallIndiumGalliumPhosphide +dustSmallNickelZincFerrite +dustSmallSiliconDioxide +dustSmallMagnesiumChloride +dustSmallSodiumSulfide +dustSmallPhosphorusPentoxide +dustSmallQuicklime +dustSmallSodiumBisulfate +dustSmallFerriteMixture +dustSmallMagnesia +dustSmallPlatinumGroupSludge +dustSmallRealgar +dustSmallSodiumBicarbonate +dustSmallPotassiumDichromate +dustSmallChromiumTrioxide +dustSmallAntimonyTrioxide +dustSmallZincite +dustSmallCupricOxide +dustSmallCobaltOxide +dustSmallArsenicTrioxide +dustSmallMassicot +dustSmallFerrosilite +dustSmallMetalMixture +dustSmallSodiumHydroxide +dustSmallBastnasite +dustSmallPentlandite +dustSmallSpodumene +dustSmallLepidolite +dustSmallGlauconiteSand +dustSmallMalachite +dustSmallMica +dustSmallBarite +dustSmallAlunite +dustSmallTalc +dustSmallSoapstone +dustSmallKyanite +dustSmallIronMagnetic +dustSmallTungstenCarbide +dustSmallEnderPearl +dustSmallPotassiumFeldspar +dustSmallNeodymiumMagnetic +dustSmallSamariumMagnetic +dustSmallManganesePhosphide +dustSmallMagnesiumDiboride +dustSmallMercuryBariumCalciumCuprate +dustSmallUraniumTriplatinum +dustSmallSamariumIronArsenicOxide +dustSmallIndiumTinBariumTitaniumCuprate +dustSmallUraniumRhodiumDinaquadide +dustSmallEnrichedNaquadahTriniumEuropiumDuranide +dustSmallRutheniumTriniumAmericiumNeutronate +dustSmallInertMetalMixture +dustSmallRhodiumSulfate +dustSmallRutheniumTetroxide +dustSmallOsmiumTetroxide +dustSmallIridiumChloride +dustSmallTitaniumTrifluoride +dustSmallCalciumPhosphide +dustSmallIndiumPhosphide +dustSmallBariumSulfide +dustSmallTriniumSulfide +dustSmallZincSulfide +dustSmallGalliumSulfide +dustSmallAntimonyTrifluoride +dustSmallEnrichedNaquadahSulfate +dustSmallNaquadriaSulfate +dustSmallPyrochlore +dustSmallRtmAlloy +dustSmallSiliconeRubber +dustSmallRawRubber +dustSmallRawStyreneButadieneRubber +dustSmallStyreneButadieneRubber +dustSmallReinforcedEpoxyResin +dustSmallPolyvinylChloride +dustSmallPolyphenyleneSulfide +dustSmallPolybenzimidazole +dustSmallPolydimethylsiloxane +dustSmallPlastic +dustSmallEpoxy +dustSmallPolycaprolactam +dustSmallPolytetrafluoroethylene +dustSmallSugar +dustSmallRubber +dustSmallCyclohexanoneOxime +dustSmallCaprolactam +dustSmallPolyvinylButyral +dustSmallBiphenyl +dustSmallGunpowder +dustSmallOilsands +dustSmallRareEarth +dustSmallStone +dustSmallGlowstone +dustSmallNetherStar +dustSmallEndstone +dustSmallNetherrack +dustSmallCollagen +dustSmallGelatin +dustSmallAgar +dustSmallCocoa +dustSmallWheat +dustSmallMeat +dustSmallWood +dustSmallPaper +dustSmallTreatedWood +dustSmallGlass +dustSmallPerlite +dustSmallBorax +dustSmallOlivine +dustSmallOpal +dustSmallAmethyst +dustSmallLapis +dustSmallBlaze +dustSmallApatite +dustSmallBlackSteel +dustSmallDamascusSteel +dustSmallTungstenSteel +dustSmallCobaltBrass +dustSmallTricalciumPhosphate +dustSmallGarnetRed +dustSmallGarnetYellow +dustSmallMarble +dustSmallGraniteBlack +dustSmallGraniteRed +dustSmallVanadiumMagnetite +dustSmallQuartzSand +dustSmallPollucite +dustSmallBentonite +dustSmallFullersEarth +dustSmallPitchblende +dustSmallMonazite +dustSmallMirabilite +dustSmallTrona +dustSmallGypsum +dustSmallZeolite +dustSmallConcrete +dustSmallSteelMagnetic +dustSmallVanadiumSteel +dustSmallPotin +dustSmallBorosilicateGlass +dustSmallAndesite +dustSmallNaquadahAlloy +dustSmallFlint +dustSmallPlatinumSludgeResidue +dustSmallPalladiumRaw +dustSmallRarestMetalMixture +dustSmallAmmoniumChloride +dustSmallRhodiumPlatedPalladium +dustSmallClay +dustSmallRedstone +dustSmallElectrotine +dustSmallEnderEye +dustSmallDiatomite +dustSmallRedSteel +dustSmallBlueSteel +dustSmallBasalt +dustSmallGraniticMineralSand +dustSmallRedrock +dustSmallGarnetSand +dustSmallHssg +dustSmallRedAlloy +dustSmallBasalticMineralSand +dustSmallHsse +dustSmallHsss +dustSmallIridiumMetalResidue +dustSmallGranite +dustSmallBrick +dustSmallFireclay +dustSmallDiorite +dustSmallBlueAlloy +dustSmallStellite100 +dustSmallWatertightSteel +dustSmallMaragingSteel300 +dustSmallHastelloyC276 +dustSmallHastelloyX +dustSmallTrinaquadalloy +dustSmallZeron100 +dustSmallTitaniumCarbide +dustSmallTantalumCarbide +dustSmallMolybdenumDisilicide +dustSmallHslaSteel +dustSmallTitaniumTungstenCarbide +dustSmallIncoloyMa956 +dustTinyDraconium +dustTinyAwakenedDraconium +dustTinyDarkSteel +dustTinyOmnium +dustTinyArdite +dustTinyManyullyn +dustTinySignalum +dustTinyConductiveIron +dustTinyEnergeticAlloy +dustTinyVibrantAlloy +dustTinyPulsatingIron +dustTinyElectricalSteel +dustTinyLumium +dustTinyEnderium +dustTinyElectrumFlux +dustTinyMithril +dustTinyCrystalMatrix +dustTinySoularium +dustTinyEndSteel +dustTinyInfinity +dustTinyMicroversium +dustTinyDraconicSuperconductor +dustTinyOsmiridium8020 +dustTinyIridosmine8020 +dustTinyKaemanite +dustTinyTungstenTrioxide +dustTinyBerylliumOxide +dustTinyNiobiumPentoxide +dustTinyTantalumPentoxide +dustTinyFluorite +dustTinyManganeseDifluoride +dustTinyMolybdenumTrioxide +dustTinyLeadChloride +dustTinyWollastonite +dustTinySodiumMetavanadate +dustTinyVanadiumPentoxide +dustTinyAmmoniumMetavanadate +dustTinyPhthalicAnhydride +dustTinyEthylanthraquinone +dustTinyGrapheneOxide +dustTinyKaptonK +dustTinyDurene +dustTinyPyromelliticDianhydride +dustTinyOxydianiline +dustTinyNaquadahOxide +dustTinyPyromorphite +dustTinyNaquadahHydroxide +dustTinySnowchestite +dustTinyCaesiumHydroxide +dustTinyLeadMetasilicate +dustTinyPlatinumMetallic +dustTinyPalladiumMetallic +dustTinyAmmoniumHexachloroplatinate +dustTinyPotassiumBisulfate +dustTinyPotassiumPyrosulfate +dustTinyPotassiumSulfate +dustTinyZincSulfate +dustTinySodiumNitrate +dustTinyRhodiumNitrate +dustTinySodiumRuthenate +dustTinySodiumPeroxide +dustTinyIridiumDioxideResidue +dustTinyAmmoniumHexachloroiridiate +dustTinyPlatinumGroupResidue +dustTinyCrudePlatinumResidue +dustTinyCrudePalladiumResidue +dustTinyIridiumGroupSludge +dustTinyCrudeRhodiumResidue +dustTinyRhodiumSalt +dustTinySodiumMethoxide +dustTinyStoneResidue +dustTinyUncommonResidue +dustTinyOxidisedResidue +dustTinyRefinedResidue +dustTinyCleanInertResidue +dustTinyTaranium +dustTinyDarmstadtite +dustTinyDulysite +dustTinyLaurite +dustTinyCuprorhodsite +dustTinyAluminium +dustTinyAmericium +dustTinyAntimony +dustTinyArsenic +dustTinyBarium +dustTinyBerkelium +dustTinyBeryllium +dustTinyBismuth +dustTinyBoron +dustTinyCaesium +dustTinyCalcium +dustTinyCalifornium +dustTinyCarbon +dustTinyCadmium +dustTinyCerium +dustTinyChrome +dustTinyCobalt +dustTinyCopper +dustTinyCurium +dustTinyDarmstadtium +dustTinyEinsteinium +dustTinyEuropium +dustTinyGallium +dustTinyGold +dustTinyIndium +dustTinyIridium +dustTinyIron +dustTinyLanthanum +dustTinyLead +dustTinyLithium +dustTinyLutetium +dustTinyMagnesium +dustTinyManganese +dustTinyMolybdenum +dustTinyNeodymium +dustTinyNeptunium +dustTinyNickel +dustTinyNiobium +dustTinyOsmium +dustTinyPalladium +dustTinyPhosphorus +dustTinyPlatinum +dustTinyPlutonium +dustTinyPlutonium239 +dustTinyPlutonium241 +dustTinyPotassium +dustTinyRhodium +dustTinyRuthenium +dustTinySamarium +dustTinySilicon +dustTinySilver +dustTinySodium +dustTinySulfur +dustTinyTantalum +dustTinyThorium +dustTinyTin +dustTinyTitanium +dustTinyTungsten +dustTinyUranium +dustTinyUranium238 +dustTinyUranium235 +dustTinyVanadium +dustTinyYttrium +dustTinyZinc +dustTinyNaquadah +dustTinyNaquadahEnriched +dustTinyNaquadria +dustTinyNeutronium +dustTinyTritanium +dustTinyDuranium +dustTinyTrinium +dustTinyCertusQuartz +dustTinyAlmandine +dustTinyAndradite +dustTinyAnnealedCopper +dustTinyAsbestos +dustTinyAsh +dustTinyBandedIron +dustTinyBatteryAlloy +dustTinyBlueTopaz +dustTinyBone +dustTinyBrass +dustTinyBronze +dustTinyBrownLimonite +dustTinyCalcite +dustTinyCassiterite +dustTinyCassiteriteSand +dustTinyChalcopyrite +dustTinyCharcoal +dustTinyChromite +dustTinyCinnabar +dustTinyCoal +dustTinyCobaltite +dustTinyCooperite +dustTinyCupronickel +dustTinyDarkAsh +dustTinyDiamond +dustTinyElectrum +dustTinyEmerald +dustTinyGalena +dustTinyGarnierite +dustTinyGreenSapphire +dustTinyGrossular +dustTinyIce +dustTinyIlmenite +dustTinyRutile +dustTinyBauxite +dustTinyInvar +dustTinyKanthal +dustTinyLazurite +dustTinyMagnalium +dustTinyMagnesite +dustTinyMagnetite +dustTinyMolybdenite +dustTinyNichrome +dustTinyNiobiumNitride +dustTinyNiobiumTitanium +dustTinyObsidian +dustTinyPhosphate +dustTinyPlatinumRaw +dustTinySterlingSilver +dustTinyRoseGold +dustTinyBlackBronze +dustTinyBismuthBronze +dustTinyBiotite +dustTinyPowellite +dustTinyPyrite +dustTinyPyrolusite +dustTinyPyrope +dustTinyRockSalt +dustTinyRuridit +dustTinyRuby +dustTinySalt +dustTinySaltpeter +dustTinySapphire +dustTinyScheelite +dustTinySodalite +dustTinyAluminiumSulfite +dustTinyTantalite +dustTinyCoke +dustTinySolderingAlloy +dustTinySpessartine +dustTinySphalerite +dustTinyStainlessSteel +dustTinySteel +dustTinyStibnite +dustTinyTetrahedrite +dustTinyTinAlloy +dustTinyTopaz +dustTinyTungstate +dustTinyUltimet +dustTinyUraninite +dustTinyUvarovite +dustTinyVanadiumGallium +dustTinyWroughtIron +dustTinyWulfenite +dustTinyYellowLimonite +dustTinyYttriumBariumCuprate +dustTinyNetherQuartz +dustTinyQuartzite +dustTinyGraphite +dustTinyGraphene +dustTinyTungsticAcid +dustTinyOsmiridium +dustTinyLithiumChloride +dustTinyCalciumChloride +dustTinyBornite +dustTinyChalcocite +dustTinyGalliumArsenide +dustTinyPotash +dustTinySodaAsh +dustTinyIndiumGalliumPhosphide +dustTinyNickelZincFerrite +dustTinySiliconDioxide +dustTinyMagnesiumChloride +dustTinySodiumSulfide +dustTinyPhosphorusPentoxide +dustTinyQuicklime +dustTinySodiumBisulfate +dustTinyFerriteMixture +dustTinyMagnesia +dustTinyPlatinumGroupSludge +dustTinyRealgar +dustTinySodiumBicarbonate +dustTinyPotassiumDichromate +dustTinyChromiumTrioxide +dustTinyAntimonyTrioxide +dustTinyZincite +dustTinyCupricOxide +dustTinyCobaltOxide +dustTinyArsenicTrioxide +dustTinyMassicot +dustTinyFerrosilite +dustTinyMetalMixture +dustTinySodiumHydroxide +dustTinyBastnasite +dustTinyPentlandite +dustTinySpodumene +dustTinyLepidolite +dustTinyGlauconiteSand +dustTinyMalachite +dustTinyMica +dustTinyBarite +dustTinyAlunite +dustTinyTalc +dustTinySoapstone +dustTinyKyanite +dustTinyIronMagnetic +dustTinyTungstenCarbide +dustTinyEnderPearl +dustTinyPotassiumFeldspar +dustTinyNeodymiumMagnetic +dustTinySamariumMagnetic +dustTinyManganesePhosphide +dustTinyMagnesiumDiboride +dustTinyMercuryBariumCalciumCuprate +dustTinyUraniumTriplatinum +dustTinySamariumIronArsenicOxide +dustTinyIndiumTinBariumTitaniumCuprate +dustTinyUraniumRhodiumDinaquadide +dustTinyEnrichedNaquadahTriniumEuropiumDuranide +dustTinyRutheniumTriniumAmericiumNeutronate +dustTinyInertMetalMixture +dustTinyRhodiumSulfate +dustTinyRutheniumTetroxide +dustTinyOsmiumTetroxide +dustTinyIridiumChloride +dustTinyTitaniumTrifluoride +dustTinyCalciumPhosphide +dustTinyIndiumPhosphide +dustTinyBariumSulfide +dustTinyTriniumSulfide +dustTinyZincSulfide +dustTinyGalliumSulfide +dustTinyAntimonyTrifluoride +dustTinyEnrichedNaquadahSulfate +dustTinyNaquadriaSulfate +dustTinyPyrochlore +dustTinyRtmAlloy +dustTinySiliconeRubber +dustTinyRawRubber +dustTinyRawStyreneButadieneRubber +dustTinyStyreneButadieneRubber +dustTinyReinforcedEpoxyResin +dustTinyPolyvinylChloride +dustTinyPolyphenyleneSulfide +dustTinyPolybenzimidazole +dustTinyPolydimethylsiloxane +dustTinyPlastic +dustTinyEpoxy +dustTinyPolycaprolactam +dustTinyPolytetrafluoroethylene +dustTinySugar +dustTinyRubber +dustTinyCyclohexanoneOxime +dustTinyCaprolactam +dustTinyPolyvinylButyral +dustTinyBiphenyl +dustTinyGunpowder +dustTinyOilsands +dustTinyRareEarth +dustTinyStone +dustTinyGlowstone +dustTinyNetherStar +dustTinyEndstone +dustTinyNetherrack +dustTinyCollagen +dustTinyGelatin +dustTinyAgar +dustTinyCocoa +dustTinyWheat +dustTinyMeat +dustTinyWood +dustTinyPaper +dustTinyTreatedWood +dustTinyGlass +dustTinyPerlite +dustTinyBorax +dustTinyOlivine +dustTinyOpal +dustTinyAmethyst +dustTinyLapis +dustTinyBlaze +dustTinyApatite +dustTinyBlackSteel +dustTinyDamascusSteel +dustTinyTungstenSteel +dustTinyCobaltBrass +dustTinyTricalciumPhosphate +dustTinyGarnetRed +dustTinyGarnetYellow +dustTinyMarble +dustTinyGraniteBlack +dustTinyGraniteRed +dustTinyVanadiumMagnetite +dustTinyQuartzSand +dustTinyPollucite +dustTinyBentonite +dustTinyFullersEarth +dustTinyPitchblende +dustTinyMonazite +dustTinyMirabilite +dustTinyTrona +dustTinyGypsum +dustTinyZeolite +dustTinyConcrete +dustTinySteelMagnetic +dustTinyVanadiumSteel +dustTinyPotin +dustTinyBorosilicateGlass +dustTinyAndesite +dustTinyNaquadahAlloy +dustTinyFlint +dustTinyPlatinumSludgeResidue +dustTinyPalladiumRaw +dustTinyRarestMetalMixture +dustTinyAmmoniumChloride +dustTinyRhodiumPlatedPalladium +dustTinyClay +dustTinyRedstone +dustTinyElectrotine +dustTinyEnderEye +dustTinyDiatomite +dustTinyRedSteel +dustTinyBlueSteel +dustTinyBasalt +dustTinyGraniticMineralSand +dustTinyRedrock +dustTinyGarnetSand +dustTinyHssg +dustTinyRedAlloy +dustTinyBasalticMineralSand +dustTinyHsse +dustTinyHsss +dustTinyIridiumMetalResidue +dustTinyGranite +dustTinyBrick +dustTinyFireclay +dustTinyDiorite +dustTinyBlueAlloy +dustTinyStellite100 +dustTinyWatertightSteel +dustTinyMaragingSteel300 +dustTinyHastelloyC276 +dustTinyHastelloyX +dustTinyTrinaquadalloy +dustTinyZeron100 +dustTinyTitaniumCarbide +dustTinyTantalumCarbide +dustTinyMolybdenumDisilicide +dustTinyHslaSteel +dustTinyTitaniumTungstenCarbide +dustTinyIncoloyMa956 +dustImpureDraconium +dustImpureOsmiridium8020 +dustImpureIridosmine8020 +dustImpureKaemanite +dustImpureFluorite +dustImpureSnowchestite +dustImpureDarmstadtite +dustImpureDulysite +dustImpureLaurite +dustImpureCuprorhodsite +dustImpureAluminium +dustImpureBeryllium +dustImpureCobalt +dustImpureCopper +dustImpureGold +dustImpureIron +dustImpureLead +dustImpureLithium +dustImpureMolybdenum +dustImpureNeodymium +dustImpureNickel +dustImpurePalladium +dustImpurePlatinum +dustImpurePlutonium +dustImpurePlutonium239 +dustImpureSilver +dustImpureSulfur +dustImpureThorium +dustImpureTin +dustImpureNaquadah +dustImpureCertusQuartz +dustImpureAlmandine +dustImpureAsbestos +dustImpureBandedIron +dustImpureBlueTopaz +dustImpureBrownLimonite +dustImpureCalcite +dustImpureCassiterite +dustImpureCassiteriteSand +dustImpureChalcopyrite +dustImpureChromite +dustImpureCinnabar +dustImpureCoal +dustImpureCobaltite +dustImpureCooperite +dustImpureDiamond +dustImpureEmerald +dustImpureGalena +dustImpureGarnierite +dustImpureGreenSapphire +dustImpureGrossular +dustImpureIlmenite +dustImpureBauxite +dustImpureLazurite +dustImpureMagnesite +dustImpureMagnetite +dustImpureMolybdenite +dustImpurePowellite +dustImpurePyrite +dustImpurePyrolusite +dustImpurePyrope +dustImpureRockSalt +dustImpureRuby +dustImpureSalt +dustImpureSaltpeter +dustImpureSapphire +dustImpureScheelite +dustImpureSodalite +dustImpureTantalite +dustImpureSpessartine +dustImpureSphalerite +dustImpureStibnite +dustImpureTetrahedrite +dustImpureTopaz +dustImpureTungstate +dustImpureUraninite +dustImpureWulfenite +dustImpureYellowLimonite +dustImpureNetherQuartz +dustImpureQuartzite +dustImpureGraphite +dustImpureBornite +dustImpureChalcocite +dustImpureRealgar +dustImpureBastnasite +dustImpurePentlandite +dustImpureSpodumene +dustImpureLepidolite +dustImpureGlauconiteSand +dustImpureMalachite +dustImpureMica +dustImpureBarite +dustImpureAlunite +dustImpureTalc +dustImpureSoapstone +dustImpureKyanite +dustImpurePyrochlore +dustImpureOilsands +dustImpureOlivine +dustImpureOpal +dustImpureAmethyst +dustImpureLapis +dustImpureApatite +dustImpureTricalciumPhosphate +dustImpureGarnetRed +dustImpureGarnetYellow +dustImpureVanadiumMagnetite +dustImpurePollucite +dustImpureBentonite +dustImpureFullersEarth +dustImpurePitchblende +dustImpureMonazite +dustImpureTrona +dustImpureGypsum +dustImpureZeolite +dustImpureRedstone +dustImpureElectrotine +dustImpureDiatomite +dustImpureGraniticMineralSand +dustImpureGarnetSand +dustImpureBasalticMineralSand +dustPureDraconium +dustPureOsmiridium8020 +dustPureIridosmine8020 +dustPureKaemanite +dustPureFluorite +dustPureSnowchestite +dustPureDarmstadtite +dustPureDulysite +dustPureLaurite +dustPureCuprorhodsite +dustPureAluminium +dustPureBeryllium +dustPureCobalt +dustPureCopper +dustPureGold +dustPureIron +dustPureLead +dustPureLithium +dustPureMolybdenum +dustPureNeodymium +dustPureNickel +dustPurePalladium +dustPurePlatinum +dustPurePlutonium +dustPurePlutonium239 +dustPureSilver +dustPureSulfur +dustPureThorium +dustPureTin +dustPureNaquadah +dustPureCertusQuartz +dustPureAlmandine +dustPureAsbestos +dustPureBandedIron +dustPureBlueTopaz +dustPureBrownLimonite +dustPureCalcite +dustPureCassiterite +dustPureCassiteriteSand +dustPureChalcopyrite +dustPureChromite +dustPureCinnabar +dustPureCoal +dustPureCobaltite +dustPureCooperite +dustPureDiamond +dustPureEmerald +dustPureGalena +dustPureGarnierite +dustPureGreenSapphire +dustPureGrossular +dustPureIlmenite +dustPureBauxite +dustPureLazurite +dustPureMagnesite +dustPureMagnetite +dustPureMolybdenite +dustPurePowellite +dustPurePyrite +dustPurePyrolusite +dustPurePyrope +dustPureRockSalt +dustPureRuby +dustPureSalt +dustPureSaltpeter +dustPureSapphire +dustPureScheelite +dustPureSodalite +dustPureTantalite +dustPureSpessartine +dustPureSphalerite +dustPureStibnite +dustPureTetrahedrite +dustPureTopaz +dustPureTungstate +dustPureUraninite +dustPureWulfenite +dustPureYellowLimonite +dustPureNetherQuartz +dustPureQuartzite +dustPureGraphite +dustPureBornite +dustPureChalcocite +dustPureRealgar +dustPureBastnasite +dustPurePentlandite +dustPureSpodumene +dustPureLepidolite +dustPureGlauconiteSand +dustPureMalachite +dustPureMica +dustPureBarite +dustPureAlunite +dustPureTalc +dustPureSoapstone +dustPureKyanite +dustPurePyrochlore +dustPureOilsands +dustPureOlivine +dustPureOpal +dustPureAmethyst +dustPureLapis +dustPureApatite +dustPureTricalciumPhosphate +dustPureGarnetRed +dustPureGarnetYellow +dustPureVanadiumMagnetite +dustPurePollucite +dustPureBentonite +dustPureFullersEarth +dustPurePitchblende +dustPureMonazite +dustPureTrona +dustPureGypsum +dustPureZeolite +dustPureRedstone +dustPureElectrotine +dustPureDiatomite +dustPureGraniticMineralSand +dustPureGarnetSand +dustPureBasalticMineralSand +crushedDraconium +crushedOsmiridium8020 +crushedIridosmine8020 +crushedKaemanite +crushedFluorite +crushedSnowchestite +crushedDarmstadtite +crushedDulysite +crushedLaurite +crushedCuprorhodsite +crushedAluminium +crushedBeryllium +crushedCobalt +crushedCopper +crushedGold +crushedIron +crushedLead +crushedLithium +crushedMolybdenum +crushedNeodymium +crushedNickel +crushedPalladium +crushedPlatinum +crushedPlutonium +crushedPlutonium239 +crushedSilver +crushedSulfur +crushedThorium +crushedTin +crushedNaquadah +crushedCertusQuartz +crushedAlmandine +crushedAsbestos +crushedBandedIron +crushedBlueTopaz +crushedBrownLimonite +crushedCalcite +crushedCassiterite +crushedCassiteriteSand +crushedChalcopyrite +crushedChromite +crushedCinnabar +crushedCoal +crushedCobaltite +crushedCooperite +crushedDiamond +crushedEmerald +crushedGalena +crushedGarnierite +crushedGreenSapphire +crushedGrossular +crushedIlmenite +crushedBauxite +crushedLazurite +crushedMagnesite +crushedMagnetite +crushedMolybdenite +crushedPowellite +crushedPyrite +crushedPyrolusite +crushedPyrope +crushedRockSalt +crushedRuby +crushedSalt +crushedSaltpeter +crushedSapphire +crushedScheelite +crushedSodalite +crushedTantalite +crushedSpessartine +crushedSphalerite +crushedStibnite +crushedTetrahedrite +crushedTopaz +crushedTungstate +crushedUraninite +crushedWulfenite +crushedYellowLimonite +crushedNetherQuartz +crushedQuartzite +crushedGraphite +crushedBornite +crushedChalcocite +crushedRealgar +crushedBastnasite +crushedPentlandite +crushedSpodumene +crushedLepidolite +crushedGlauconiteSand +crushedMalachite +crushedMica +crushedBarite +crushedAlunite +crushedTalc +crushedSoapstone +crushedKyanite +crushedPyrochlore +crushedOilsands +crushedOlivine +crushedOpal +crushedAmethyst +crushedLapis +crushedApatite +crushedTricalciumPhosphate +crushedGarnetRed +crushedGarnetYellow +crushedVanadiumMagnetite +crushedPollucite +crushedBentonite +crushedFullersEarth +crushedPitchblende +crushedMonazite +crushedTrona +crushedGypsum +crushedZeolite +crushedRedstone +crushedElectrotine +crushedDiatomite +crushedGraniticMineralSand +crushedGarnetSand +crushedBasalticMineralSand +crushedPurifiedDraconium +crushedPurifiedOsmiridium8020 +crushedPurifiedIridosmine8020 +crushedPurifiedKaemanite +crushedPurifiedFluorite +crushedPurifiedSnowchestite +crushedPurifiedDarmstadtite +crushedPurifiedDulysite +crushedPurifiedLaurite +crushedPurifiedCuprorhodsite +crushedPurifiedAluminium +crushedPurifiedBeryllium +crushedPurifiedCobalt +crushedPurifiedCopper +crushedPurifiedGold +crushedPurifiedIron +crushedPurifiedLead +crushedPurifiedLithium +crushedPurifiedMolybdenum +crushedPurifiedNeodymium +crushedPurifiedNickel +crushedPurifiedPalladium +crushedPurifiedPlatinum +crushedPurifiedPlutonium +crushedPurifiedPlutonium239 +crushedPurifiedSilver +crushedPurifiedSulfur +crushedPurifiedThorium +crushedPurifiedTin +crushedPurifiedNaquadah +crushedPurifiedCertusQuartz +crushedPurifiedAlmandine +crushedPurifiedAsbestos +crushedPurifiedBandedIron +crushedPurifiedBlueTopaz +crushedPurifiedBrownLimonite +crushedPurifiedCalcite +crushedPurifiedCassiterite +crushedPurifiedCassiteriteSand +crushedPurifiedChalcopyrite +crushedPurifiedChromite +crushedPurifiedCinnabar +crushedPurifiedCoal +crushedPurifiedCobaltite +crushedPurifiedCooperite +crushedPurifiedDiamond +crushedPurifiedEmerald +crushedPurifiedGalena +crushedPurifiedGarnierite +crushedPurifiedGreenSapphire +crushedPurifiedGrossular +crushedPurifiedIlmenite +crushedPurifiedBauxite +crushedPurifiedLazurite +crushedPurifiedMagnesite +crushedPurifiedMagnetite +crushedPurifiedMolybdenite +crushedPurifiedPowellite +crushedPurifiedPyrite +crushedPurifiedPyrolusite +crushedPurifiedPyrope +crushedPurifiedRockSalt +crushedPurifiedRuby +crushedPurifiedSalt +crushedPurifiedSaltpeter +crushedPurifiedSapphire +crushedPurifiedScheelite +crushedPurifiedSodalite +crushedPurifiedTantalite +crushedPurifiedSpessartine +crushedPurifiedSphalerite +crushedPurifiedStibnite +crushedPurifiedTetrahedrite +crushedPurifiedTopaz +crushedPurifiedTungstate +crushedPurifiedUraninite +crushedPurifiedWulfenite +crushedPurifiedYellowLimonite +crushedPurifiedNetherQuartz +crushedPurifiedQuartzite +crushedPurifiedGraphite +crushedPurifiedBornite +crushedPurifiedChalcocite +crushedPurifiedRealgar +crushedPurifiedBastnasite +crushedPurifiedPentlandite +crushedPurifiedSpodumene +crushedPurifiedLepidolite +crushedPurifiedGlauconiteSand +crushedPurifiedMalachite +crushedPurifiedMica +crushedPurifiedBarite +crushedPurifiedAlunite +crushedPurifiedTalc +crushedPurifiedSoapstone +crushedPurifiedKyanite +crushedPurifiedPyrochlore +crushedPurifiedOilsands +crushedPurifiedOlivine +crushedPurifiedOpal +crushedPurifiedAmethyst +crushedPurifiedLapis +crushedPurifiedApatite +crushedPurifiedTricalciumPhosphate +crushedPurifiedGarnetRed +crushedPurifiedGarnetYellow +crushedPurifiedVanadiumMagnetite +crushedPurifiedPollucite +crushedPurifiedBentonite +crushedPurifiedFullersEarth +crushedPurifiedPitchblende +crushedPurifiedMonazite +crushedPurifiedTrona +crushedPurifiedGypsum +crushedPurifiedZeolite +crushedPurifiedRedstone +crushedPurifiedElectrotine +crushedPurifiedDiatomite +crushedPurifiedGraniticMineralSand +crushedPurifiedGarnetSand +crushedPurifiedBasalticMineralSand +crushedCentrifugedDraconium +crushedCentrifugedOsmiridium8020 +crushedCentrifugedIridosmine8020 +crushedCentrifugedKaemanite +crushedCentrifugedFluorite +crushedCentrifugedSnowchestite +crushedCentrifugedDarmstadtite +crushedCentrifugedDulysite +crushedCentrifugedLaurite +crushedCentrifugedCuprorhodsite +crushedCentrifugedAluminium +crushedCentrifugedBeryllium +crushedCentrifugedCobalt +crushedCentrifugedCopper +crushedCentrifugedGold +crushedCentrifugedIron +crushedCentrifugedLead +crushedCentrifugedLithium +crushedCentrifugedMolybdenum +crushedCentrifugedNeodymium +crushedCentrifugedNickel +crushedCentrifugedPalladium +crushedCentrifugedPlatinum +crushedCentrifugedPlutonium +crushedCentrifugedPlutonium239 +crushedCentrifugedSilver +crushedCentrifugedSulfur +crushedCentrifugedThorium +crushedCentrifugedTin +crushedCentrifugedNaquadah +crushedCentrifugedCertusQuartz +crushedCentrifugedAlmandine +crushedCentrifugedAsbestos +crushedCentrifugedBandedIron +crushedCentrifugedBlueTopaz +crushedCentrifugedBrownLimonite +crushedCentrifugedCalcite +crushedCentrifugedCassiterite +crushedCentrifugedCassiteriteSand +crushedCentrifugedChalcopyrite +crushedCentrifugedChromite +crushedCentrifugedCinnabar +crushedCentrifugedCoal +crushedCentrifugedCobaltite +crushedCentrifugedCooperite +crushedCentrifugedDiamond +crushedCentrifugedEmerald +crushedCentrifugedGalena +crushedCentrifugedGarnierite +crushedCentrifugedGreenSapphire +crushedCentrifugedGrossular +crushedCentrifugedIlmenite +crushedCentrifugedBauxite +crushedCentrifugedLazurite +crushedCentrifugedMagnesite +crushedCentrifugedMagnetite +crushedCentrifugedMolybdenite +crushedCentrifugedPowellite +crushedCentrifugedPyrite +crushedCentrifugedPyrolusite +crushedCentrifugedPyrope +crushedCentrifugedRockSalt +crushedCentrifugedRuby +crushedCentrifugedSalt +crushedCentrifugedSaltpeter +crushedCentrifugedSapphire +crushedCentrifugedScheelite +crushedCentrifugedSodalite +crushedCentrifugedTantalite +crushedCentrifugedSpessartine +crushedCentrifugedSphalerite +crushedCentrifugedStibnite +crushedCentrifugedTetrahedrite +crushedCentrifugedTopaz +crushedCentrifugedTungstate +crushedCentrifugedUraninite +crushedCentrifugedWulfenite +crushedCentrifugedYellowLimonite +crushedCentrifugedNetherQuartz +crushedCentrifugedQuartzite +crushedCentrifugedGraphite +crushedCentrifugedBornite +crushedCentrifugedChalcocite +crushedCentrifugedRealgar +crushedCentrifugedBastnasite +crushedCentrifugedPentlandite +crushedCentrifugedSpodumene +crushedCentrifugedLepidolite +crushedCentrifugedGlauconiteSand +crushedCentrifugedMalachite +crushedCentrifugedMica +crushedCentrifugedBarite +crushedCentrifugedAlunite +crushedCentrifugedTalc +crushedCentrifugedSoapstone +crushedCentrifugedKyanite +crushedCentrifugedPyrochlore +crushedCentrifugedOilsands +crushedCentrifugedOlivine +crushedCentrifugedOpal +crushedCentrifugedAmethyst +crushedCentrifugedLapis +crushedCentrifugedApatite +crushedCentrifugedTricalciumPhosphate +crushedCentrifugedGarnetRed +crushedCentrifugedGarnetYellow +crushedCentrifugedVanadiumMagnetite +crushedCentrifugedPollucite +crushedCentrifugedBentonite +crushedCentrifugedFullersEarth +crushedCentrifugedPitchblende +crushedCentrifugedMonazite +crushedCentrifugedTrona +crushedCentrifugedGypsum +crushedCentrifugedZeolite +crushedCentrifugedRedstone +crushedCentrifugedElectrotine +crushedCentrifugedDiatomite +crushedCentrifugedGraniticMineralSand +crushedCentrifugedGarnetSand +crushedCentrifugedBasalticMineralSand +gemDulysite +gemLaurite +gemCertusQuartz +gemAlmandine +gemAndradite +gemBlueTopaz +gemCinnabar +gemGreenSapphire +gemGrossular +gemRutile +gemLazurite +gemPyrope +gemRockSalt +gemSalt +gemSodalite +gemCoke +gemSpessartine +gemTopaz +gemUvarovite +gemQuartzite +gemRealgar +gemMalachite +gemSugar +gemLapotron +gemGlass +gemOlivine +gemOpal +gemApatite +gemGarnetRed +gemGarnetYellow +gemMonazite +gemFlawlessDulysite +gemFlawlessLaurite +gemFlawlessCertusQuartz +gemFlawlessAlmandine +gemFlawlessAndradite +gemFlawlessBlueTopaz +gemFlawlessCinnabar +gemFlawlessCoal +gemFlawlessDiamond +gemFlawlessEmerald +gemFlawlessGreenSapphire +gemFlawlessGrossular +gemFlawlessRutile +gemFlawlessLazurite +gemFlawlessPyrope +gemFlawlessRockSalt +gemFlawlessRuby +gemFlawlessSalt +gemFlawlessSapphire +gemFlawlessSodalite +gemFlawlessCoke +gemFlawlessSpessartine +gemFlawlessTopaz +gemFlawlessUvarovite +gemFlawlessNetherQuartz +gemFlawlessQuartzite +gemFlawlessRealgar +gemFlawlessMalachite +gemFlawlessGlass +gemFlawlessOlivine +gemFlawlessOpal +gemFlawlessAmethyst +gemFlawlessLapis +gemFlawlessApatite +gemFlawlessGarnetRed +gemFlawlessGarnetYellow +gemFlawlessMonazite +gemExquisiteDulysite +gemExquisiteLaurite +gemExquisiteCertusQuartz +gemExquisiteAlmandine +gemExquisiteAndradite +gemExquisiteBlueTopaz +gemExquisiteCinnabar +gemExquisiteCoal +gemExquisiteDiamond +gemExquisiteEmerald +gemExquisiteGreenSapphire +gemExquisiteGrossular +gemExquisiteRutile +gemExquisiteLazurite +gemExquisitePyrope +gemExquisiteRockSalt +gemExquisiteRuby +gemExquisiteSalt +gemExquisiteSapphire +gemExquisiteSodalite +gemExquisiteCoke +gemExquisiteSpessartine +gemExquisiteTopaz +gemExquisiteUvarovite +gemExquisiteNetherQuartz +gemExquisiteQuartzite +gemExquisiteRealgar +gemExquisiteMalachite +gemExquisiteGlass +gemExquisiteOlivine +gemExquisiteOpal +gemExquisiteAmethyst +gemExquisiteLapis +gemExquisiteApatite +gemExquisiteGarnetRed +gemExquisiteGarnetYellow +gemExquisiteMonazite +ingotAwakenedDraconium +ingotOmnium +ingotMicroversium +ingotDraconicSuperconductor +ingotBerylliumOxide +ingotKaptonK +ingotTaranium +ingotAmericium +ingotAntimony +ingotBerkelium +ingotBismuth +ingotCalifornium +ingotChrome +ingotCurium +ingotDarmstadtium +ingotEinsteinium +ingotEuropium +ingotGallium +ingotIndium +ingotLutetium +ingotMolybdenum +ingotNeodymium +ingotNeptunium +ingotNiobium +ingotPalladium +ingotPlutonium +ingotRhodium +ingotRuthenium +ingotSamarium +ingotTantalum +ingotThorium +ingotTungsten +ingotUranium +ingotVanadium +ingotYttrium +ingotZinc +ingotNaquadah +ingotNaquadahEnriched +ingotNaquadria +ingotNeutronium +ingotTritanium +ingotDuranium +ingotTrinium +ingotAnnealedCopper +ingotBatteryAlloy +ingotCupronickel +ingotKanthal +ingotMagnalium +ingotNichrome +ingotNiobiumNitride +ingotNiobiumTitanium +ingotSterlingSilver +ingotRoseGold +ingotBlackBronze +ingotBismuthBronze +ingotRuridit +ingotSolderingAlloy +ingotStainlessSteel +ingotTinAlloy +ingotUltimet +ingotVanadiumGallium +ingotWroughtIron +ingotYttriumBariumCuprate +ingotGalliumArsenide +ingotIndiumGalliumPhosphide +ingotNickelZincFerrite +ingotIronMagnetic +ingotTungstenCarbide +ingotNeodymiumMagnetic +ingotSamariumMagnetic +ingotManganesePhosphide +ingotMercuryBariumCalciumCuprate +ingotUraniumTriplatinum +ingotSamariumIronArsenicOxide +ingotIndiumTinBariumTitaniumCuprate +ingotUraniumRhodiumDinaquadide +ingotEnrichedNaquadahTriniumEuropiumDuranide +ingotRutheniumTriniumAmericiumNeutronate +ingotRtmAlloy +ingotSiliconeRubber +ingotRawRubber +ingotStyreneButadieneRubber +ingotReinforcedEpoxyResin +ingotPolyvinylChloride +ingotPolyphenyleneSulfide +ingotPolybenzimidazole +ingotPlastic +ingotEpoxy +ingotPolycaprolactam +ingotPolytetrafluoroethylene +ingotRubber +ingotPolyvinylButyral +ingotBlackSteel +ingotDamascusSteel +ingotTungstenSteel +ingotCobaltBrass +ingotSteelMagnetic +ingotVanadiumSteel +ingotPotin +ingotBorosilicateGlass +ingotNaquadahAlloy +ingotRhodiumPlatedPalladium +ingotRedSteel +ingotBlueSteel +ingotHssg +ingotHsse +ingotHsss +ingotBlueAlloy +ingotStellite100 +ingotWatertightSteel +ingotMaragingSteel300 +ingotHastelloyC276 +ingotHastelloyX +ingotTrinaquadalloy +ingotZeron100 +ingotTitaniumCarbide +ingotTantalumCarbide +ingotMolybdenumDisilicide +ingotHslaSteel +ingotTitaniumTungstenCarbide +ingotIncoloyMa956 +ingotHotDraconium +ingotHotSignalum +ingotHotLumium +ingotHotEnderium +ingotHotTaranium +ingotHotEuropium +ingotHotIridium +ingotHotNiobium +ingotHotOsmium +ingotHotPalladium +ingotHotRhodium +ingotHotRuthenium +ingotHotSamarium +ingotHotSilicon +ingotHotTitanium +ingotHotTungsten +ingotHotVanadium +ingotHotYttrium +ingotHotNaquadah +ingotHotNaquadahEnriched +ingotHotNaquadria +ingotHotTrinium +ingotHotKanthal +ingotHotNichrome +ingotHotNiobiumNitride +ingotHotNiobiumTitanium +ingotHotBlackBronze +ingotHotRuridit +ingotHotUltimet +ingotHotVanadiumGallium +ingotHotYttriumBariumCuprate +ingotHotOsmiridium +ingotHotTungstenCarbide +ingotHotMagnesiumDiboride +ingotHotMercuryBariumCalciumCuprate +ingotHotUraniumTriplatinum +ingotHotSamariumIronArsenicOxide +ingotHotIndiumTinBariumTitaniumCuprate +ingotHotUraniumRhodiumDinaquadide +ingotHotEnrichedNaquadahTriniumEuropiumDuranide +ingotHotRutheniumTriniumAmericiumNeutronate +ingotHotRtmAlloy +ingotHotTungstenSteel +ingotHotNaquadahAlloy +ingotHotRhodiumPlatedPalladium +ingotHotHssg +ingotHotHsse +ingotHotHsss +ingotHotStellite100 +ingotHotWatertightSteel +ingotHotMaragingSteel300 +ingotHotHastelloyC276 +ingotHotHastelloyX +ingotHotTrinaquadalloy +ingotHotZeron100 +ingotHotTitaniumCarbide +ingotHotTantalumCarbide +ingotHotMolybdenumDisilicide +ingotHotTitaniumTungstenCarbide +ingotHotIncoloyMa956 +plateDraconium +plateAwakenedDraconium +plateDarkSteel +plateArdite +plateManyullyn +plateConductiveIron +plateEnergeticAlloy +plateVibrantAlloy +platePulsatingIron +plateElectricalSteel +plateCrystalMatrix +plateSoularium +plateEndSteel +plateMicroversium +plateKaptonK +plateTaranium +plateAluminium +plateAmericium +plateBeryllium +plateChrome +plateCobalt +plateDarmstadtium +plateEuropium +plateGallium +plateManganese +plateMolybdenum +plateNeodymium +plateOsmium +platePalladium +platePlutonium241 +plateRhodium +plateRuthenium +plateTantalum +plateThorium +plateTungsten +plateUranium +plateUranium238 +plateUranium235 +plateZinc +plateNaquadah +plateNaquadahEnriched +plateNaquadria +plateNeutronium +plateTritanium +plateDuranium +plateTrinium +plateCertusQuartz +plateAnnealedCopper +plateBatteryAlloy +plateBlueTopaz +plateBrass +plateCupronickel +plateDiamond +plateEmerald +plateGreenSapphire +plateKanthal +plateLazurite +plateMagnalium +plateNichrome +plateNiobiumNitride +plateNiobiumTitanium +plateObsidian +plateSterlingSilver +plateRoseGold +plateBlackBronze +plateBismuthBronze +plateRuridit +plateRuby +plateSapphire +plateSodalite +plateStainlessSteel +plateTinAlloy +plateTopaz +plateUltimet +plateVanadiumGallium +plateWroughtIron +plateYttriumBariumCuprate +plateNetherQuartz +plateQuartzite +plateGraphene +plateOsmiridium +plateGalliumArsenide +plateIndiumGalliumPhosphide +plateTungstenCarbide +plateEnderPearl +plateIndiumTinBariumTitaniumCuprate +plateUraniumRhodiumDinaquadide +plateEnrichedNaquadahTriniumEuropiumDuranide +plateSiliconeRubber +plateStyreneButadieneRubber +plateReinforcedEpoxyResin +platePolyvinylChloride +platePolyphenyleneSulfide +platePolybenzimidazole +platePlastic +plateEpoxy +platePolycaprolactam +platePolytetrafluoroethylene +plateRubber +platePolyvinylButyral +plateStone +plateGlowstone +plateNetherStar +plateWood +plateTreatedWood +plateGlass +plateOlivine +plateOpal +plateAmethyst +plateLapis +plateBlackSteel +plateDamascusSteel +plateTungstenSteel +plateCobaltBrass +plateGarnetRed +plateGarnetYellow +plateVanadiumSteel +platePotin +plateNaquadahAlloy +plateRhodiumPlatedPalladium +plateRedstone +plateEnderEye +plateRedSteel +plateBlueSteel +plateHssg +plateRedAlloy +plateHsse +plateHsss +plateBlueAlloy +plateStellite100 +plateWatertightSteel +plateMaragingSteel300 +plateHastelloyC276 +plateHastelloyX +plateTrinaquadalloy +plateZeron100 +plateTitaniumCarbide +plateTantalumCarbide +plateMolybdenumDisilicide +plateHslaSteel +plateTitaniumTungstenCarbide +plateIncoloyMa956 +plateDoubleDraconium +plateDoubleAwakenedDraconium +plateDoubleSignalum +plateDoubleEnergeticAlloy +plateDoubleVibrantAlloy +plateDoubleLumium +plateDoubleEnderium +plateDoubleElectrumFlux +plateDoubleCrystalMatrix +plateDoubleAluminium +plateDoubleAmericium +plateDoubleBeryllium +plateDoubleChrome +plateDoubleCobalt +plateDoubleCopper +plateDoubleDarmstadtium +plateDoubleEuropium +plateDoubleGold +plateDoubleIridium +plateDoubleLead +plateDoubleNickel +plateDoubleOsmium +plateDoublePlatinum +plateDoublePlutonium241 +plateDoubleSilver +plateDoubleTin +plateDoubleTitanium +plateDoubleTungsten +plateDoubleNaquadah +plateDoubleNaquadria +plateDoubleNeutronium +plateDoubleDuranium +plateDoubleTrinium +plateDoubleBrass +plateDoubleBronze +plateDoubleCupronickel +plateDoubleElectrum +plateDoubleMagnalium +plateDoubleNiobiumTitanium +plateDoubleSterlingSilver +plateDoubleRoseGold +plateDoubleBlackBronze +plateDoubleStainlessSteel +plateDoubleSteel +plateDoubleTinAlloy +plateDoubleUltimet +plateDoubleWroughtIron +plateDoubleOsmiridium +plateDoubleTungstenCarbide +plateDoubleTungstenSteel +plateDoubleCobaltBrass +plateDoubleVanadiumSteel +plateDoublePotin +plateDoubleNaquadahAlloy +plateDoubleRhodiumPlatedPalladium +plateDoubleWatertightSteel +plateDoubleTrinaquadalloy +plateDoubleMolybdenumDisilicide +plateDoubleHslaSteel +plateDoubleIncoloyMa956 +plateDenseDraconium +plateDenseSignalum +plateDenseTaranium +plateDenseDarmstadtium +plateDenseIridium +plateDenseNeutronium +plateDenseTritanium +plateDenseTrinium +plateDenseObsidian +plateDenseTungstenSteel +plateDenseNaquadahAlloy +plateDenseRhodiumPlatedPalladium +plateDenseTantalumCarbide +foilLumium +foilEnderium +foilAluminium +foilAmericium +foilCobalt +foilCopper +foilEuropium +foilGallium +foilGold +foilIridium +foilLead +foilManganese +foilMolybdenum +foilOsmium +foilPalladium +foilPlatinum +foilRhodium +foilRuthenium +foilSilicon +foilSilver +foilTantalum +foilTin +foilTitanium +foilTungsten +foilZinc +foilNaquadah +foilNaquadahEnriched +foilNaquadria +foilTritanium +foilDuranium +foilTrinium +foilAnnealedCopper +foilBronze +foilCupronickel +foilElectrum +foilNiobiumNitride +foilNiobiumTitanium +foilRuridit +foilStainlessSteel +foilSteel +foilVanadiumGallium +foilWroughtIron +foilYttriumBariumCuprate +foilGraphene +foilOsmiridium +foilTungstenCarbide +foilIndiumTinBariumTitaniumCuprate +foilUraniumRhodiumDinaquadide +foilEnrichedNaquadahTriniumEuropiumDuranide +foilSiliconeRubber +foilStyreneButadieneRubber +foilPolyvinylChloride +foilPolyphenyleneSulfide +foilPolybenzimidazole +foilPlastic +foilPolycaprolactam +foilPolytetrafluoroethylene +foilRubber +foilBlackSteel +foilTungstenSteel +foilVanadiumSteel +foilNaquadahAlloy +foilHssg +foilRedAlloy +foilHsss +stickDraconium +stickAwakenedDraconium +stickDarkSteel +stickSignalum +stickConductiveIron +stickEnergeticAlloy +stickVibrantAlloy +stickPulsatingIron +stickElectricalSteel +stickLumium +stickEnderium +stickElectrumFlux +stickMithril +stickEndSteel +stickMicroversium +stickBerylliumOxide +stickAluminium +stickAmericium +stickBerkelium +stickChrome +stickCobalt +stickDarmstadtium +stickEuropium +stickGold +stickLead +stickManganese +stickMolybdenum +stickNeodymium +stickNickel +stickOsmium +stickPalladium +stickPlatinum +stickPlutonium241 +stickRhodium +stickRuthenium +stickSamarium +stickSilver +stickThorium +stickTin +stickTungsten +stickUranium +stickUranium238 +stickUranium235 +stickZinc +stickNaquadah +stickNaquadahEnriched +stickNaquadria +stickNeutronium +stickTritanium +stickDuranium +stickTrinium +stickAnnealedCopper +stickBatteryAlloy +stickBlueTopaz +stickBrass +stickBronze +stickCupronickel +stickDiamond +stickElectrum +stickEmerald +stickGreenSapphire +stickInvar +stickKanthal +stickLazurite +stickMagnalium +stickNichrome +stickNiobiumNitride +stickNiobiumTitanium +stickSterlingSilver +stickRoseGold +stickBlackBronze +stickBismuthBronze +stickRuridit +stickRuby +stickSapphire +stickSodalite +stickStainlessSteel +stickTinAlloy +stickTopaz +stickUltimet +stickVanadiumGallium +stickWroughtIron +stickYttriumBariumCuprate +stickOsmiridium +stickNickelZincFerrite +stickIronMagnetic +stickTungstenCarbide +stickNeodymiumMagnetic +stickSamariumMagnetic +stickRtmAlloy +stickSiliconeRubber +stickStyreneButadieneRubber +stickPolyvinylChloride +stickPolyphenyleneSulfide +stickPolytetrafluoroethylene +stickRubber +stickStone +stickTreatedWood +stickOlivine +stickOpal +stickAmethyst +stickLapis +stickApatite +stickBlackSteel +stickDamascusSteel +stickTungstenSteel +stickCobaltBrass +stickGarnetRed +stickGarnetYellow +stickSteelMagnetic +stickVanadiumSteel +stickPotin +stickNaquadahAlloy +stickRhodiumPlatedPalladium +stickRedSteel +stickBlueSteel +stickHssg +stickRedAlloy +stickHsse +stickHsss +stickBlueAlloy +stickWatertightSteel +stickMaragingSteel300 +stickHastelloyC276 +stickHastelloyX +stickMolybdenumDisilicide +stickHslaSteel +stickIncoloyMa956 +stickLongAluminium +stickLongCopper +stickLongDarmstadtium +stickLongEuropium +stickLongGold +stickLongIridium +stickLongIron +stickLongLead +stickLongOsmium +stickLongPlatinum +stickLongRhodium +stickLongSamarium +stickLongSilver +stickLongTin +stickLongTitanium +stickLongTungsten +stickLongNaquadah +stickLongNeutronium +stickLongTritanium +stickLongAnnealedCopper +stickLongBrass +stickLongBronze +stickLongCupronickel +stickLongElectrum +stickLongInvar +stickLongKanthal +stickLongMagnalium +stickLongNichrome +stickLongNiobiumTitanium +stickLongSterlingSilver +stickLongRoseGold +stickLongBlackBronze +stickLongBismuthBronze +stickLongRuridit +stickLongStainlessSteel +stickLongSteel +stickLongTinAlloy +stickLongUltimet +stickLongVanadiumGallium +stickLongWroughtIron +stickLongYttriumBariumCuprate +stickLongOsmiridium +stickLongTungstenCarbide +stickLongSamariumMagnetic +stickLongRtmAlloy +stickLongWood +stickLongTungstenSteel +stickLongCobaltBrass +stickLongVanadiumSteel +stickLongPotin +stickLongNaquadahAlloy +stickLongRhodiumPlatedPalladium +stickLongHssg +stickLongHsse +stickLongHsss +stickLongMolybdenumDisilicide +stickLongHslaSteel +boltVibrantAlloy +boltAluminium +boltChrome +boltDarmstadtium +boltGold +boltIridium +boltIron +boltLead +boltManganese +boltMolybdenum +boltNeodymium +boltOsmium +boltPlatinum +boltRhodium +boltSilver +boltTin +boltTitanium +boltTungsten +boltNaquadah +boltNaquadahEnriched +boltNaquadria +boltNeutronium +boltTritanium +boltTrinium +boltAnnealedCopper +boltBrass +boltBronze +boltDiamond +boltElectrum +boltInvar +boltMagnalium +boltNiobiumTitanium +boltSterlingSilver +boltRoseGold +boltBlackBronze +boltBismuthBronze +boltRuridit +boltStainlessSteel +boltSteel +boltTinAlloy +boltUltimet +boltWroughtIron +boltYttriumBariumCuprate +boltOsmiridium +boltIronMagnetic +boltTungstenCarbide +boltRubber +boltWood +boltApatite +boltTungstenSteel +boltCobaltBrass +boltVanadiumSteel +boltPotin +boltNaquadahAlloy +boltRhodiumPlatedPalladium +boltHssg +boltRedAlloy +boltHsse +boltHsss +boltBlueAlloy +screwVibrantAlloy +screwAluminium +screwChrome +screwDarmstadtium +screwGold +screwIridium +screwIron +screwLead +screwManganese +screwMolybdenum +screwNeodymium +screwOsmium +screwPlatinum +screwRhodium +screwSilver +screwTin +screwTitanium +screwTungsten +screwNaquadah +screwNaquadahEnriched +screwNaquadria +screwNeutronium +screwTritanium +screwTrinium +screwAnnealedCopper +screwBrass +screwBronze +screwDiamond +screwElectrum +screwInvar +screwMagnalium +screwNiobiumTitanium +screwSterlingSilver +screwRoseGold +screwBlackBronze +screwBismuthBronze +screwRuridit +screwStainlessSteel +screwSteel +screwTinAlloy +screwUltimet +screwWroughtIron +screwYttriumBariumCuprate +screwOsmiridium +screwIronMagnetic +screwTungstenCarbide +screwRubber +screwWood +screwApatite +screwTungstenSteel +screwCobaltBrass +screwVanadiumSteel +screwPotin +screwNaquadahAlloy +screwRhodiumPlatedPalladium +screwHssg +screwRedAlloy +screwHsse +screwHsss +screwBlueAlloy +ringAwakenedDraconium +ringBerylliumOxide +ringAluminium +ringChrome +ringDarmstadtium +ringGold +ringIridium +ringIron +ringLead +ringPlatinum +ringSilver +ringTin +ringTitanium +ringZinc +ringTritanium +ringBronze +ringElectrum +ringRoseGold +ringStainlessSteel +ringSteel +ringWroughtIron +ringOsmiridium +ringNickelZincFerrite +ringSiliconeRubber +ringStyreneButadieneRubber +ringRubber +ringTungstenSteel +ringNaquadahAlloy +ringRhodiumPlatedPalladium +ringHsse +ringHsss +ringMolybdenumDisilicide +nuggetAwakenedDraconium +nuggetOmnium +nuggetArdite +nuggetManyullyn +nuggetCrystalMatrix +nuggetInfinity +nuggetMicroversium +nuggetDraconicSuperconductor +nuggetBerylliumOxide +nuggetKaptonK +nuggetTaranium +nuggetAluminium +nuggetAmericium +nuggetAntimony +nuggetBerkelium +nuggetBeryllium +nuggetBismuth +nuggetCalifornium +nuggetChrome +nuggetCobalt +nuggetCurium +nuggetDarmstadtium +nuggetEinsteinium +nuggetEuropium +nuggetGallium +nuggetIndium +nuggetLutetium +nuggetManganese +nuggetMolybdenum +nuggetNeodymium +nuggetNeptunium +nuggetNiobium +nuggetOsmium +nuggetPalladium +nuggetPlutonium +nuggetRhodium +nuggetRuthenium +nuggetSamarium +nuggetTantalum +nuggetThorium +nuggetTungsten +nuggetUranium +nuggetVanadium +nuggetYttrium +nuggetZinc +nuggetNaquadah +nuggetNaquadahEnriched +nuggetNaquadria +nuggetNeutronium +nuggetTritanium +nuggetDuranium +nuggetTrinium +nuggetAnnealedCopper +nuggetBatteryAlloy +nuggetBrass +nuggetCupronickel +nuggetKanthal +nuggetMagnalium +nuggetNichrome +nuggetNiobiumNitride +nuggetNiobiumTitanium +nuggetSterlingSilver +nuggetRoseGold +nuggetBlackBronze +nuggetBismuthBronze +nuggetRuridit +nuggetSolderingAlloy +nuggetStainlessSteel +nuggetTinAlloy +nuggetUltimet +nuggetVanadiumGallium +nuggetWroughtIron +nuggetYttriumBariumCuprate +nuggetGraphite +nuggetOsmiridium +nuggetGalliumArsenide +nuggetIndiumGalliumPhosphide +nuggetNickelZincFerrite +nuggetIronMagnetic +nuggetTungstenCarbide +nuggetNeodymiumMagnetic +nuggetSamariumMagnetic +nuggetManganesePhosphide +nuggetMagnesiumDiboride +nuggetMercuryBariumCalciumCuprate +nuggetUraniumTriplatinum +nuggetSamariumIronArsenicOxide +nuggetIndiumTinBariumTitaniumCuprate +nuggetUraniumRhodiumDinaquadide +nuggetEnrichedNaquadahTriniumEuropiumDuranide +nuggetRutheniumTriniumAmericiumNeutronate +nuggetRtmAlloy +nuggetSiliconeRubber +nuggetRawRubber +nuggetStyreneButadieneRubber +nuggetReinforcedEpoxyResin +nuggetPolyvinylChloride +nuggetPolyphenyleneSulfide +nuggetPolybenzimidazole +nuggetPlastic +nuggetEpoxy +nuggetPolycaprolactam +nuggetPolytetrafluoroethylene +nuggetRubber +nuggetPolyvinylButyral +nuggetBlackSteel +nuggetDamascusSteel +nuggetTungstenSteel +nuggetCobaltBrass +nuggetSteelMagnetic +nuggetVanadiumSteel +nuggetPotin +nuggetBorosilicateGlass +nuggetNaquadahAlloy +nuggetRhodiumPlatedPalladium +nuggetRedSteel +nuggetBlueSteel +nuggetHssg +nuggetRedAlloy +nuggetHsse +nuggetHsss +nuggetBlueAlloy +nuggetStellite100 +nuggetWatertightSteel +nuggetMaragingSteel300 +nuggetHastelloyC276 +nuggetHastelloyX +nuggetTrinaquadalloy +nuggetZeron100 +nuggetTitaniumCarbide +nuggetTantalumCarbide +nuggetMolybdenumDisilicide +nuggetHslaSteel +nuggetTitaniumTungstenCarbide +nuggetIncoloyMa956 +roundNeutronium +roundTritanium +roundOsmiridium +roundHsss +springAluminium +springCopper +springEuropium +springGold +springIron +springLead +springTin +springTungsten +springNaquadah +springNeutronium +springTritanium +springCupronickel +springKanthal +springNichrome +springNiobiumTitanium +springSteel +springVanadiumGallium +springYttriumBariumCuprate +springRtmAlloy +springTungstenSteel +springNaquadahAlloy +springHssg +springMolybdenumDisilicide +springHslaSteel +springSmallAluminium +springSmallCopper +springSmallGold +springSmallIron +springSmallLead +springSmallTin +springSmallTungsten +springSmallNiobiumTitanium +springSmallSteel +springSmallVanadiumGallium +springSmallYttriumBariumCuprate +gearDraconium +gearAwakenedDraconium +gearConductiveIron +gearEnergeticAlloy +gearVibrantAlloy +gearPulsatingIron +gearElectricalSteel +gearEndSteel +gearAluminium +gearRhodium +gearRuthenium +gearTungsten +gearNaquadria +gearNeutronium +gearTritanium +gearDuranium +gearTrinium +gearSterlingSilver +gearRoseGold +gearBlackBronze +gearRuridit +gearStainlessSteel +gearUltimet +gearWroughtIron +gearOsmiridium +gearTungstenCarbide +gearSiliconeRubber +gearRubber +gearBlackSteel +gearDamascusSteel +gearTungstenSteel +gearCobaltBrass +gearVanadiumSteel +gearPotin +gearNaquadahAlloy +gearRedSteel +gearBlueSteel +gearHssg +gearHsse +gearHsss +gearSmallAluminium +gearSmallDarmstadtium +gearSmallIron +gearSmallTitanium +gearSmallTritanium +gearSmallBronze +gearSmallStainlessSteel +gearSmallSteel +gearSmallOsmiridium +gearSmallTungstenCarbide +gearSmallTungstenSteel +gearSmallNaquadahAlloy +gearSmallRhodiumPlatedPalladium +gearSmallHssg +gearSmallHsss +wireFineLumium +wireFineEnderium +wireFineAluminium +wireFineAmericium +wireFineCobalt +wireFineCopper +wireFineEuropium +wireFineGold +wireFineIridium +wireFineLead +wireFinePalladium +wireFinePlatinum +wireFineRhodium +wireFineSilver +wireFineTantalum +wireFineTin +wireFineTitanium +wireFineZinc +wireFineNaquadah +wireFineNaquadria +wireFineTritanium +wireFineAnnealedCopper +wireFineCupronickel +wireFineElectrum +wireFineNiobiumTitanium +wireFineRuridit +wireFineStainlessSteel +wireFineSteel +wireFineYttriumBariumCuprate +wireFineOsmiridium +wireFineIndiumTinBariumTitaniumCuprate +wireFineUraniumRhodiumDinaquadide +wireFineEnrichedNaquadahTriniumEuropiumDuranide +wireFineBlackSteel +wireFineTungstenSteel +wireFineBorosilicateGlass +wireFineHssg +wireFineRedAlloy +rotorChrome +rotorDarmstadtium +rotorIridium +rotorIron +rotorLead +rotorTin +rotorTitanium +rotorBronze +rotorStainlessSteel +rotorSteel +rotorOsmiridium +rotorTungstenSteel +rotorNaquadahAlloy +rotorRhodiumPlatedPalladium +rotorHsss +lensBlueTopaz +lensDiamond +lensEmerald +lensRuby +lensSapphire +lensTopaz +lensEnderPearl +lensNetherStar +lensGlass +turbineBladeAluminium +turbineBladeChrome +turbineBladeIridium +turbineBladeIron +turbineBladeManganese +turbineBladeMolybdenum +turbineBladeNeodymium +turbineBladeOsmium +turbineBladeTitanium +turbineBladeTungsten +turbineBladeNaquadah +turbineBladeNeutronium +turbineBladeTritanium +turbineBladeBrass +turbineBladeBronze +turbineBladeInvar +turbineBladeMagnalium +turbineBladeSterlingSilver +turbineBladeRoseGold +turbineBladeBlackBronze +turbineBladeBismuthBronze +turbineBladeStainlessSteel +turbineBladeSteel +turbineBladeUltimet +turbineBladeWroughtIron +turbineBladeOsmiridium +turbineBladeTungstenCarbide +turbineBladeTungstenSteel +turbineBladeCobaltBrass +turbineBladeVanadiumSteel +turbineBladeNaquadahAlloy +turbineBladeRhodiumPlatedPalladium +turbineBladeHssg +turbineBladeHsse +turbineBladeHsss +toolHeadDrillEndSteel +toolHeadDrillAluminium +toolHeadDrillIron +toolHeadDrillTitanium +toolHeadDrillNeutronium +toolHeadDrillDuranium +toolHeadDrillBronze +toolHeadDrillDiamond +toolHeadDrillInvar +toolHeadDrillSterlingSilver +toolHeadDrillRoseGold +toolHeadDrillStainlessSteel +toolHeadDrillSteel +toolHeadDrillUltimet +toolHeadDrillWroughtIron +toolHeadDrillTungstenCarbide +toolHeadDrillDamascusSteel +toolHeadDrillTungstenSteel +toolHeadDrillCobaltBrass +toolHeadDrillVanadiumSteel +toolHeadDrillNaquadahAlloy +toolHeadDrillRedSteel +toolHeadDrillBlueSteel +toolHeadDrillHsse +toolHeadChainsawEndSteel +toolHeadChainsawAluminium +toolHeadChainsawIron +toolHeadChainsawTitanium +toolHeadChainsawNeutronium +toolHeadChainsawDuranium +toolHeadChainsawBronze +toolHeadChainsawDiamond +toolHeadChainsawInvar +toolHeadChainsawSterlingSilver +toolHeadChainsawRoseGold +toolHeadChainsawStainlessSteel +toolHeadChainsawSteel +toolHeadChainsawUltimet +toolHeadChainsawWroughtIron +toolHeadChainsawTungstenCarbide +toolHeadChainsawDamascusSteel +toolHeadChainsawTungstenSteel +toolHeadChainsawCobaltBrass +toolHeadChainsawVanadiumSteel +toolHeadChainsawNaquadahAlloy +toolHeadChainsawRedSteel +toolHeadChainsawBlueSteel +toolHeadChainsawHsse +toolHeadWrenchEndSteel +toolHeadWrenchAluminium +toolHeadWrenchIron +toolHeadWrenchTitanium +toolHeadWrenchNeutronium +toolHeadWrenchDuranium +toolHeadWrenchBronze +toolHeadWrenchDiamond +toolHeadWrenchInvar +toolHeadWrenchSterlingSilver +toolHeadWrenchRoseGold +toolHeadWrenchStainlessSteel +toolHeadWrenchSteel +toolHeadWrenchUltimet +toolHeadWrenchWroughtIron +toolHeadWrenchTungstenCarbide +toolHeadWrenchDamascusSteel +toolHeadWrenchTungstenSteel +toolHeadWrenchCobaltBrass +toolHeadWrenchVanadiumSteel +toolHeadWrenchNaquadahAlloy +toolHeadWrenchRedSteel +toolHeadWrenchBlueSteel +toolHeadWrenchHsse +toolHeadBuzzSawEndSteel +toolHeadBuzzSawAluminium +toolHeadBuzzSawIron +toolHeadBuzzSawTitanium +toolHeadBuzzSawNeutronium +toolHeadBuzzSawDuranium +toolHeadBuzzSawBronze +toolHeadBuzzSawDiamond +toolHeadBuzzSawInvar +toolHeadBuzzSawSterlingSilver +toolHeadBuzzSawRoseGold +toolHeadBuzzSawStainlessSteel +toolHeadBuzzSawSteel +toolHeadBuzzSawUltimet +toolHeadBuzzSawWroughtIron +toolHeadBuzzSawTungstenCarbide +toolHeadBuzzSawDamascusSteel +toolHeadBuzzSawTungstenSteel +toolHeadBuzzSawCobaltBrass +toolHeadBuzzSawVanadiumSteel +toolHeadBuzzSawNaquadahAlloy +toolHeadBuzzSawRedSteel +toolHeadBuzzSawBlueSteel +toolHeadBuzzSawHsse +toolHeadScrewdriverAluminium +toolHeadScrewdriverIron +toolHeadScrewdriverTitanium +toolHeadScrewdriverNeutronium +toolHeadScrewdriverBronze +toolHeadScrewdriverInvar +toolHeadScrewdriverSterlingSilver +toolHeadScrewdriverRoseGold +toolHeadScrewdriverStainlessSteel +toolHeadScrewdriverSteel +toolHeadScrewdriverUltimet +toolHeadScrewdriverWroughtIron +toolHeadScrewdriverTungstenCarbide +toolHeadScrewdriverTungstenSteel +toolHeadScrewdriverCobaltBrass +toolHeadScrewdriverVanadiumSteel +toolHeadScrewdriverNaquadahAlloy +toolHeadScrewdriverHsse +gemPerfectDiamond +gemPerfectEmerald +gemPerfectRuby +gemPerfectTopaz +craftingLensMagenta +craftingLensGlass +craftingLensLime +craftingLensYellow +craftingLensBrown +craftingLensGray +craftingLensBlue +craftingLensLightGray +craftingLensLightBlue +craftingLensBlack +craftingLensGreen +craftingLensPurple +craftingLensCyan +craftingLensRed +craftingLensPink +craftingLensOrange +toolSword +toolPickaxe +toolShovel +toolAxe +toolSaw +craftingToolSaw +toolHammer +craftingToolHardHammer +toolMallet +craftingToolSoftHammer +toolMiningHammer +toolSpade +toolWrench +craftingToolWrench +toolFile +craftingToolFile +toolCrowbar +craftingToolCrowbar +toolScrewdriver +craftingToolScrewdriver +toolMortar +craftingToolMortar +toolWireCutter +craftingToolWireCutter +toolScythe +toolKnife +craftingToolKnife +toolButcheryKnife +craftingToolButcheryKnife +toolDrill +toolChainsaw +toolBuzzsaw +toolPlunger +lampGtWhite +lampGtOrange +lampGtMagenta +lampGtLightBlue +lampGtYellow +lampGtLime +lampGtPink +lampGtGray +lampGt +lampGtCyan +lampGtPurple +lampGtBlue +lampGtBrown +lampGtGreen +lampGtRed +lampGtBlack +blockPlastic +blockArdite +blockMagnalium +blockLutetium +blockNiobiumTitanium +blockBerkelium +blockWroughtIron +blockTrinium +blockPolybenzimidazole +blockPolyvinylButyral +blockCupronickel +blockReinforcedEpoxyResin +blockThorium +blockTrinaquadalloy +blockDamascusSteel +blockNaquadahEnriched +blockSugar +blockNaquadahAlloy +blockCobaltBrass +blockIndium +blockOmnium +blockHsss +blockYttrium +blockIndiumGalliumPhosphide +blockBlueTopaz +blockVanadium +blockFlint +blockWatertightSteel +blockAndradite +blockEuropium +blockBlueAlloy +blockNiobium +blockCurium +blockSodalite +blockOpal +blockStainlessSteel +blockAmethyst +blockAnnealedCopper +blockMagnesiumDiboride +blockDarmstadtium +blockNickelZincFerrite +blockStellite100 +blockIncoloyMa956 +blockRockSalt +blockPolyphenyleneSulfide +blockManganesePhosphide +blockPlutonium241 +blockRuridit +blockSalt +blockEinsteinium +blockNeodymium +blockSterlingSilver +blockUranium235 +blockAntimony +blockEnderEye +blockSapphire +blockTitaniumCarbide +blockManyullyn +blockIronMagnetic +blockTopaz +blockVanadiumSteel +blockVanadiumGallium +blockTinAlloy +blockGreenSapphire +blockSamariumMagnetic +blockOsmium +blockSolderingAlloy +blockEnrichedNaquadahTriniumEuropiumDuranide +blockHastelloyC276 +blockCoke +blockDulysite +blockRedSteel +blockBorosilicateGlass +blockRedAlloy +blockBatteryAlloy +blockCinnabar +blockTungsten +blockRhodium +blockRtmAlloy +blockPolytetrafluoroethylene +blockRutheniumTriniumAmericiumNeutronate +blockRhodiumPlatedPalladium +blockRoseGold +blockPlutonium +blockPotin +blockUraniumRhodiumDinaquadide +blockKanthal +blockCertusQuartz +blockChrome +blockTungstenSteel +blockSilicon +blockNaquadah +blockMicroversium +blockGalliumArsenide +blockRealgar +blockOlivine +blockZeron100 +blockLaurite +blockCalifornium +blockYttriumBariumCuprate +blockNichrome +blockMonazite +blockUvarovite +blockGarnetRed +blockGallium +blockNiobiumNitride +blockBlueSteel +blockTungstenCarbide +blockNeptunium +blockNaquadria +blockRuby +blockZinc +blockEpoxy +blockTaranium +blockPyrope +blockKaptonK +blockUltimet +blockGrossular +blockBlackSteel +blockQuartzite +blockPolycaprolactam +blockApatite +blockBismuthBronze +blockRuthenium +blockSteelMagnetic +blockHastelloyX +blockAlmandine +blockUraniumTriplatinum +blockMalachite +blockTantalum +blockPolyvinylChloride +blockTantalumCarbide +blockBrass +blockHssg +blockAluminium +blockRutile +blockTitaniumTungstenCarbide +blockMaragingSteel300 +blockSpessartine +blockBismuth +blockTritanium +blockRubber +blockGarnetYellow +blockMolybdenumDisilicide +blockNeutronium +blockMercuryBariumCalciumCuprate +blockOsmiridium +blockSiliconeRubber +blockSamariumIronArsenicOxide +blockMolybdenum +blockNeodymiumMagnetic +blockBerylliumOxide +blockHslaSteel +blockEnderPearl +blockNull +blockDuranium +blockPalladium +blockAwakenedDraconium +blockAmericium +blockStyreneButadieneRubber +blockSamarium +blockLazurite +blockDraconicSuperconductor +blockBlackBronze +blockIndiumTinBariumTitaniumCuprate +blockHsse +frameGtBlackSteel +frameGtWatertightSteel +frameGtDarkSteel +frameGtInvar +frameGtWood +frameGtTungsten +frameGtHslaSteel +frameGtPolytetrafluoroethylene +frameGtEuropium +frameGtBronze +frameGtNull +frameGtMaragingSteel300 +frameGtBerkelium +frameGtHastelloyX +frameGtTreatedWood +frameGtIridium +frameGtTungstenSteel +frameGtAwakenedDraconium +frameGtStainlessSteel +frameGtBlueSteel +frameGtTungstenCarbide +frameGtTritanium +frameGtTitanium +frameGtIncoloyMa956 +frameGtNaquadahAlloy +frameGtMicroversium +frameGtHastelloyC276 +frameGtRuridit +frameGtNeutronium +frameGtUltimet +frameGtHsse +frameGtHssg +frameGtHsss +frameGtAluminium +frameGtBrass +frameGtSteel +oreNetherrackDraconium +oreEndstoneDraconium +oreSandDraconium +oreRedSandDraconium +oreGraniteDraconium +oreDioriteDraconium +oreAndesiteDraconium +oreBlackgraniteDraconium +oreRedgraniteDraconium +oreMarbleDraconium +oreBasaltDraconium +oreOsmiridium8020 +oreNetherrackOsmiridium8020 +oreEndstoneOsmiridium8020 +oreSandOsmiridium8020 +oreRedSandOsmiridium8020 +oreGraniteOsmiridium8020 +oreDioriteOsmiridium8020 +oreAndesiteOsmiridium8020 +oreBlackgraniteOsmiridium8020 +oreRedgraniteOsmiridium8020 +oreMarbleOsmiridium8020 +oreBasaltOsmiridium8020 +oreIridosmine8020 +oreNetherrackIridosmine8020 +oreEndstoneIridosmine8020 +oreSandIridosmine8020 +oreRedSandIridosmine8020 +oreGraniteIridosmine8020 +oreDioriteIridosmine8020 +oreAndesiteIridosmine8020 +oreBlackgraniteIridosmine8020 +oreRedgraniteIridosmine8020 +oreMarbleIridosmine8020 +oreBasaltIridosmine8020 +oreKaemanite +oreNetherrackKaemanite +oreEndstoneKaemanite +oreSandKaemanite +oreRedSandKaemanite +oreGraniteKaemanite +oreDioriteKaemanite +oreAndesiteKaemanite +oreBlackgraniteKaemanite +oreRedgraniteKaemanite +oreMarbleKaemanite +oreBasaltKaemanite +oreFluorite +oreNetherrackFluorite +oreEndstoneFluorite +oreSandFluorite +oreRedSandFluorite +oreGraniteFluorite +oreDioriteFluorite +oreAndesiteFluorite +oreBlackgraniteFluorite +oreRedgraniteFluorite +oreMarbleFluorite +oreBasaltFluorite +oreSnowchestite +oreNetherrackSnowchestite +oreEndstoneSnowchestite +oreSandSnowchestite +oreRedSandSnowchestite +oreGraniteSnowchestite +oreDioriteSnowchestite +oreAndesiteSnowchestite +oreBlackgraniteSnowchestite +oreRedgraniteSnowchestite +oreMarbleSnowchestite +oreBasaltSnowchestite +oreDarmstadtite +oreNetherrackDarmstadtite +oreEndstoneDarmstadtite +oreSandDarmstadtite +oreRedSandDarmstadtite +oreGraniteDarmstadtite +oreDioriteDarmstadtite +oreAndesiteDarmstadtite +oreBlackgraniteDarmstadtite +oreRedgraniteDarmstadtite +oreMarbleDarmstadtite +oreBasaltDarmstadtite +oreDulysite +oreNetherrackDulysite +oreEndstoneDulysite +oreSandDulysite +oreRedSandDulysite +oreGraniteDulysite +oreDioriteDulysite +oreAndesiteDulysite +oreBlackgraniteDulysite +oreRedgraniteDulysite +oreMarbleDulysite +oreBasaltDulysite +oreLaurite +oreNetherrackLaurite +oreEndstoneLaurite +oreSandLaurite +oreRedSandLaurite +oreGraniteLaurite +oreDioriteLaurite +oreAndesiteLaurite +oreBlackgraniteLaurite +oreRedgraniteLaurite +oreMarbleLaurite +oreBasaltLaurite +oreCuprorhodsite +oreNetherrackCuprorhodsite +oreEndstoneCuprorhodsite +oreSandCuprorhodsite +oreRedSandCuprorhodsite +oreGraniteCuprorhodsite +oreDioriteCuprorhodsite +oreAndesiteCuprorhodsite +oreBlackgraniteCuprorhodsite +oreRedgraniteCuprorhodsite +oreMarbleCuprorhodsite +oreBasaltCuprorhodsite +oreAluminium +oreNetherrackAluminium +oreEndstoneAluminium +oreSandAluminium +oreRedSandAluminium +oreGraniteAluminium +oreDioriteAluminium +oreAndesiteAluminium +oreBlackgraniteAluminium +oreRedgraniteAluminium +oreMarbleAluminium +oreBasaltAluminium +oreBeryllium +oreNetherrackBeryllium +oreEndstoneBeryllium +oreSandBeryllium +oreRedSandBeryllium +oreGraniteBeryllium +oreDioriteBeryllium +oreAndesiteBeryllium +oreBlackgraniteBeryllium +oreRedgraniteBeryllium +oreMarbleBeryllium +oreBasaltBeryllium +oreNetherrackCobalt +oreEndstoneCobalt +oreSandCobalt +oreRedSandCobalt +oreGraniteCobalt +oreDioriteCobalt +oreAndesiteCobalt +oreBlackgraniteCobalt +oreRedgraniteCobalt +oreMarbleCobalt +oreBasaltCobalt +oreNetherrackCopper +oreEndstoneCopper +oreSandCopper +oreRedSandCopper +oreGraniteCopper +oreDioriteCopper +oreAndesiteCopper +oreBlackgraniteCopper +oreRedgraniteCopper +oreMarbleCopper +oreBasaltCopper +oreNetherrackGold +oreEndstoneGold +oreSandGold +oreRedSandGold +oreGraniteGold +oreDioriteGold +oreAndesiteGold +oreBlackgraniteGold +oreRedgraniteGold +oreMarbleGold +oreBasaltGold +oreNetherrackIron +oreEndstoneIron +oreSandIron +oreRedSandIron +oreGraniteIron +oreDioriteIron +oreAndesiteIron +oreBlackgraniteIron +oreRedgraniteIron +oreMarbleIron +oreBasaltIron +oreNetherrackLead +oreEndstoneLead +oreSandLead +oreRedSandLead +oreGraniteLead +oreDioriteLead +oreAndesiteLead +oreBlackgraniteLead +oreRedgraniteLead +oreMarbleLead +oreBasaltLead +oreLithium +oreNetherrackLithium +oreEndstoneLithium +oreSandLithium +oreRedSandLithium +oreGraniteLithium +oreDioriteLithium +oreAndesiteLithium +oreBlackgraniteLithium +oreRedgraniteLithium +oreMarbleLithium +oreBasaltLithium +oreMolybdenum +oreNetherrackMolybdenum +oreEndstoneMolybdenum +oreSandMolybdenum +oreRedSandMolybdenum +oreGraniteMolybdenum +oreDioriteMolybdenum +oreAndesiteMolybdenum +oreBlackgraniteMolybdenum +oreRedgraniteMolybdenum +oreMarbleMolybdenum +oreBasaltMolybdenum +oreNeodymium +oreNetherrackNeodymium +oreEndstoneNeodymium +oreSandNeodymium +oreRedSandNeodymium +oreGraniteNeodymium +oreDioriteNeodymium +oreAndesiteNeodymium +oreBlackgraniteNeodymium +oreRedgraniteNeodymium +oreMarbleNeodymium +oreBasaltNeodymium +oreNetherrackNickel +oreEndstoneNickel +oreSandNickel +oreRedSandNickel +oreGraniteNickel +oreDioriteNickel +oreAndesiteNickel +oreBlackgraniteNickel +oreRedgraniteNickel +oreMarbleNickel +oreBasaltNickel +orePalladium +oreNetherrackPalladium +oreEndstonePalladium +oreSandPalladium +oreRedSandPalladium +oreGranitePalladium +oreDioritePalladium +oreAndesitePalladium +oreBlackgranitePalladium +oreRedgranitePalladium +oreMarblePalladium +oreBasaltPalladium +oreNetherrackPlatinum +oreEndstonePlatinum +oreSandPlatinum +oreRedSandPlatinum +oreGranitePlatinum +oreDioritePlatinum +oreAndesitePlatinum +oreBlackgranitePlatinum +oreRedgranitePlatinum +oreMarblePlatinum +oreBasaltPlatinum +orePlutonium +oreNetherrackPlutonium +oreEndstonePlutonium +oreSandPlutonium +oreRedSandPlutonium +oreGranitePlutonium +oreDioritePlutonium +oreAndesitePlutonium +oreBlackgranitePlutonium +oreRedgranitePlutonium +oreMarblePlutonium +oreBasaltPlutonium +oreNetherrackSilver +oreEndstoneSilver +oreSandSilver +oreRedSandSilver +oreGraniteSilver +oreDioriteSilver +oreAndesiteSilver +oreBlackgraniteSilver +oreRedgraniteSilver +oreMarbleSilver +oreBasaltSilver +oreSulfur +oreNetherrackSulfur +oreEndstoneSulfur +oreSandSulfur +oreRedSandSulfur +oreGraniteSulfur +oreDioriteSulfur +oreAndesiteSulfur +oreBlackgraniteSulfur +oreRedgraniteSulfur +oreMarbleSulfur +oreBasaltSulfur +oreThorium +oreNetherrackThorium +oreEndstoneThorium +oreSandThorium +oreRedSandThorium +oreGraniteThorium +oreDioriteThorium +oreAndesiteThorium +oreBlackgraniteThorium +oreRedgraniteThorium +oreMarbleThorium +oreBasaltThorium +oreNetherrackTin +oreEndstoneTin +oreSandTin +oreRedSandTin +oreGraniteTin +oreDioriteTin +oreAndesiteTin +oreBlackgraniteTin +oreRedgraniteTin +oreMarbleTin +oreBasaltTin +oreNaquadah +oreNetherrackNaquadah +oreEndstoneNaquadah +oreSandNaquadah +oreRedSandNaquadah +oreGraniteNaquadah +oreDioriteNaquadah +oreAndesiteNaquadah +oreBlackgraniteNaquadah +oreRedgraniteNaquadah +oreMarbleNaquadah +oreBasaltNaquadah +oreCertusQuartz +oreNetherrackCertusQuartz +oreEndstoneCertusQuartz +oreSandCertusQuartz +oreRedSandCertusQuartz +oreGraniteCertusQuartz +oreDioriteCertusQuartz +oreAndesiteCertusQuartz +oreBlackgraniteCertusQuartz +oreRedgraniteCertusQuartz +oreMarbleCertusQuartz +oreBasaltCertusQuartz +oreAlmandine +oreNetherrackAlmandine +oreEndstoneAlmandine +oreSandAlmandine +oreRedSandAlmandine +oreGraniteAlmandine +oreDioriteAlmandine +oreAndesiteAlmandine +oreBlackgraniteAlmandine +oreRedgraniteAlmandine +oreMarbleAlmandine +oreBasaltAlmandine +oreAsbestos +oreNetherrackAsbestos +oreEndstoneAsbestos +oreSandAsbestos +oreRedSandAsbestos +oreGraniteAsbestos +oreDioriteAsbestos +oreAndesiteAsbestos +oreBlackgraniteAsbestos +oreRedgraniteAsbestos +oreMarbleAsbestos +oreBasaltAsbestos +oreBandedIron +oreNetherrackBandedIron +oreEndstoneBandedIron +oreSandBandedIron +oreRedSandBandedIron +oreGraniteBandedIron +oreDioriteBandedIron +oreAndesiteBandedIron +oreBlackgraniteBandedIron +oreRedgraniteBandedIron +oreMarbleBandedIron +oreBasaltBandedIron +oreBlueTopaz +oreNetherrackBlueTopaz +oreEndstoneBlueTopaz +oreSandBlueTopaz +oreRedSandBlueTopaz +oreGraniteBlueTopaz +oreDioriteBlueTopaz +oreAndesiteBlueTopaz +oreBlackgraniteBlueTopaz +oreRedgraniteBlueTopaz +oreMarbleBlueTopaz +oreBasaltBlueTopaz +oreBrownLimonite +oreNetherrackBrownLimonite +oreEndstoneBrownLimonite +oreSandBrownLimonite +oreRedSandBrownLimonite +oreGraniteBrownLimonite +oreDioriteBrownLimonite +oreAndesiteBrownLimonite +oreBlackgraniteBrownLimonite +oreRedgraniteBrownLimonite +oreMarbleBrownLimonite +oreBasaltBrownLimonite +oreCalcite +oreNetherrackCalcite +oreEndstoneCalcite +oreSandCalcite +oreRedSandCalcite +oreGraniteCalcite +oreDioriteCalcite +oreAndesiteCalcite +oreBlackgraniteCalcite +oreRedgraniteCalcite +oreMarbleCalcite +oreBasaltCalcite +oreCassiterite +oreNetherrackCassiterite +oreEndstoneCassiterite +oreSandCassiterite +oreRedSandCassiterite +oreGraniteCassiterite +oreDioriteCassiterite +oreAndesiteCassiterite +oreBlackgraniteCassiterite +oreRedgraniteCassiterite +oreMarbleCassiterite +oreBasaltCassiterite +oreCassiteriteSand +oreNetherrackCassiteriteSand +oreEndstoneCassiteriteSand +oreSandCassiteriteSand +oreRedSandCassiteriteSand +oreGraniteCassiteriteSand +oreDioriteCassiteriteSand +oreAndesiteCassiteriteSand +oreBlackgraniteCassiteriteSand +oreRedgraniteCassiteriteSand +oreMarbleCassiteriteSand +oreBasaltCassiteriteSand +oreChalcopyrite +oreNetherrackChalcopyrite +oreEndstoneChalcopyrite +oreSandChalcopyrite +oreRedSandChalcopyrite +oreGraniteChalcopyrite +oreDioriteChalcopyrite +oreAndesiteChalcopyrite +oreBlackgraniteChalcopyrite +oreRedgraniteChalcopyrite +oreMarbleChalcopyrite +oreBasaltChalcopyrite +oreChromite +oreNetherrackChromite +oreEndstoneChromite +oreSandChromite +oreRedSandChromite +oreGraniteChromite +oreDioriteChromite +oreAndesiteChromite +oreBlackgraniteChromite +oreRedgraniteChromite +oreMarbleChromite +oreBasaltChromite +oreCinnabar +oreNetherrackCinnabar +oreEndstoneCinnabar +oreSandCinnabar +oreRedSandCinnabar +oreGraniteCinnabar +oreDioriteCinnabar +oreAndesiteCinnabar +oreBlackgraniteCinnabar +oreRedgraniteCinnabar +oreMarbleCinnabar +oreBasaltCinnabar +oreNetherrackCoal +oreEndstoneCoal +oreSandCoal +oreRedSandCoal +oreGraniteCoal +oreDioriteCoal +oreAndesiteCoal +oreBlackgraniteCoal +oreRedgraniteCoal +oreMarbleCoal +oreBasaltCoal +oreCobaltite +oreNetherrackCobaltite +oreEndstoneCobaltite +oreSandCobaltite +oreRedSandCobaltite +oreGraniteCobaltite +oreDioriteCobaltite +oreAndesiteCobaltite +oreBlackgraniteCobaltite +oreRedgraniteCobaltite +oreMarbleCobaltite +oreBasaltCobaltite +oreCooperite +oreNetherrackCooperite +oreEndstoneCooperite +oreSandCooperite +oreRedSandCooperite +oreGraniteCooperite +oreDioriteCooperite +oreAndesiteCooperite +oreBlackgraniteCooperite +oreRedgraniteCooperite +oreMarbleCooperite +oreBasaltCooperite +oreNetherrackDiamond +oreEndstoneDiamond +oreSandDiamond +oreRedSandDiamond +oreGraniteDiamond +oreDioriteDiamond +oreAndesiteDiamond +oreBlackgraniteDiamond +oreRedgraniteDiamond +oreMarbleDiamond +oreBasaltDiamond +oreNetherrackEmerald +oreEndstoneEmerald +oreSandEmerald +oreRedSandEmerald +oreGraniteEmerald +oreDioriteEmerald +oreAndesiteEmerald +oreBlackgraniteEmerald +oreRedgraniteEmerald +oreMarbleEmerald +oreBasaltEmerald +oreGalena +oreNetherrackGalena +oreEndstoneGalena +oreSandGalena +oreRedSandGalena +oreGraniteGalena +oreDioriteGalena +oreAndesiteGalena +oreBlackgraniteGalena +oreRedgraniteGalena +oreMarbleGalena +oreBasaltGalena +oreGarnierite +oreNetherrackGarnierite +oreEndstoneGarnierite +oreSandGarnierite +oreRedSandGarnierite +oreGraniteGarnierite +oreDioriteGarnierite +oreAndesiteGarnierite +oreBlackgraniteGarnierite +oreRedgraniteGarnierite +oreMarbleGarnierite +oreBasaltGarnierite +oreGreenSapphire +oreNetherrackGreenSapphire +oreEndstoneGreenSapphire +oreSandGreenSapphire +oreRedSandGreenSapphire +oreGraniteGreenSapphire +oreDioriteGreenSapphire +oreAndesiteGreenSapphire +oreBlackgraniteGreenSapphire +oreRedgraniteGreenSapphire +oreMarbleGreenSapphire +oreBasaltGreenSapphire +oreGrossular +oreNetherrackGrossular +oreEndstoneGrossular +oreSandGrossular +oreRedSandGrossular +oreGraniteGrossular +oreDioriteGrossular +oreAndesiteGrossular +oreBlackgraniteGrossular +oreRedgraniteGrossular +oreMarbleGrossular +oreBasaltGrossular +oreIlmenite +oreNetherrackIlmenite +oreEndstoneIlmenite +oreSandIlmenite +oreRedSandIlmenite +oreGraniteIlmenite +oreDioriteIlmenite +oreAndesiteIlmenite +oreBlackgraniteIlmenite +oreRedgraniteIlmenite +oreMarbleIlmenite +oreBasaltIlmenite +oreBauxite +oreNetherrackBauxite +oreEndstoneBauxite +oreSandBauxite +oreRedSandBauxite +oreGraniteBauxite +oreDioriteBauxite +oreAndesiteBauxite +oreBlackgraniteBauxite +oreRedgraniteBauxite +oreMarbleBauxite +oreBasaltBauxite +oreLazurite +oreNetherrackLazurite +oreEndstoneLazurite +oreSandLazurite +oreRedSandLazurite +oreGraniteLazurite +oreDioriteLazurite +oreAndesiteLazurite +oreBlackgraniteLazurite +oreRedgraniteLazurite +oreMarbleLazurite +oreBasaltLazurite +oreMagnesite +oreNetherrackMagnesite +oreEndstoneMagnesite +oreSandMagnesite +oreRedSandMagnesite +oreGraniteMagnesite +oreDioriteMagnesite +oreAndesiteMagnesite +oreBlackgraniteMagnesite +oreRedgraniteMagnesite +oreMarbleMagnesite +oreBasaltMagnesite +oreMagnetite +oreNetherrackMagnetite +oreEndstoneMagnetite +oreSandMagnetite +oreRedSandMagnetite +oreGraniteMagnetite +oreDioriteMagnetite +oreAndesiteMagnetite +oreBlackgraniteMagnetite +oreRedgraniteMagnetite +oreMarbleMagnetite +oreBasaltMagnetite +oreMolybdenite +oreNetherrackMolybdenite +oreEndstoneMolybdenite +oreSandMolybdenite +oreRedSandMolybdenite +oreGraniteMolybdenite +oreDioriteMolybdenite +oreAndesiteMolybdenite +oreBlackgraniteMolybdenite +oreRedgraniteMolybdenite +oreMarbleMolybdenite +oreBasaltMolybdenite +orePowellite +oreNetherrackPowellite +oreEndstonePowellite +oreSandPowellite +oreRedSandPowellite +oreGranitePowellite +oreDioritePowellite +oreAndesitePowellite +oreBlackgranitePowellite +oreRedgranitePowellite +oreMarblePowellite +oreBasaltPowellite +orePyrite +oreNetherrackPyrite +oreEndstonePyrite +oreSandPyrite +oreRedSandPyrite +oreGranitePyrite +oreDioritePyrite +oreAndesitePyrite +oreBlackgranitePyrite +oreRedgranitePyrite +oreMarblePyrite +oreBasaltPyrite +orePyrolusite +oreNetherrackPyrolusite +oreEndstonePyrolusite +oreSandPyrolusite +oreRedSandPyrolusite +oreGranitePyrolusite +oreDioritePyrolusite +oreAndesitePyrolusite +oreBlackgranitePyrolusite +oreRedgranitePyrolusite +oreMarblePyrolusite +oreBasaltPyrolusite +orePyrope +oreNetherrackPyrope +oreEndstonePyrope +oreSandPyrope +oreRedSandPyrope +oreGranitePyrope +oreDioritePyrope +oreAndesitePyrope +oreBlackgranitePyrope +oreRedgranitePyrope +oreMarblePyrope +oreBasaltPyrope +oreRockSalt +oreNetherrackRockSalt +oreEndstoneRockSalt +oreSandRockSalt +oreRedSandRockSalt +oreGraniteRockSalt +oreDioriteRockSalt +oreAndesiteRockSalt +oreBlackgraniteRockSalt +oreRedgraniteRockSalt +oreMarbleRockSalt +oreBasaltRockSalt +oreRuby +oreNetherrackRuby +oreEndstoneRuby +oreSandRuby +oreRedSandRuby +oreGraniteRuby +oreDioriteRuby +oreAndesiteRuby +oreBlackgraniteRuby +oreRedgraniteRuby +oreMarbleRuby +oreBasaltRuby +oreSalt +oreNetherrackSalt +oreEndstoneSalt +oreSandSalt +oreRedSandSalt +oreGraniteSalt +oreDioriteSalt +oreAndesiteSalt +oreBlackgraniteSalt +oreRedgraniteSalt +oreMarbleSalt +oreBasaltSalt +oreSaltpeter +oreNetherrackSaltpeter +oreEndstoneSaltpeter +oreSandSaltpeter +oreRedSandSaltpeter +oreGraniteSaltpeter +oreDioriteSaltpeter +oreAndesiteSaltpeter +oreBlackgraniteSaltpeter +oreRedgraniteSaltpeter +oreMarbleSaltpeter +oreBasaltSaltpeter +oreSapphire +oreNetherrackSapphire +oreEndstoneSapphire +oreSandSapphire +oreRedSandSapphire +oreGraniteSapphire +oreDioriteSapphire +oreAndesiteSapphire +oreBlackgraniteSapphire +oreRedgraniteSapphire +oreMarbleSapphire +oreBasaltSapphire +oreScheelite +oreNetherrackScheelite +oreEndstoneScheelite +oreSandScheelite +oreRedSandScheelite +oreGraniteScheelite +oreDioriteScheelite +oreAndesiteScheelite +oreBlackgraniteScheelite +oreRedgraniteScheelite +oreMarbleScheelite +oreBasaltScheelite +oreSodalite +oreNetherrackSodalite +oreEndstoneSodalite +oreSandSodalite +oreRedSandSodalite +oreGraniteSodalite +oreDioriteSodalite +oreAndesiteSodalite +oreBlackgraniteSodalite +oreRedgraniteSodalite +oreMarbleSodalite +oreBasaltSodalite +oreTantalite +oreNetherrackTantalite +oreEndstoneTantalite +oreSandTantalite +oreRedSandTantalite +oreGraniteTantalite +oreDioriteTantalite +oreAndesiteTantalite +oreBlackgraniteTantalite +oreRedgraniteTantalite +oreMarbleTantalite +oreBasaltTantalite +oreSpessartine +oreNetherrackSpessartine +oreEndstoneSpessartine +oreSandSpessartine +oreRedSandSpessartine +oreGraniteSpessartine +oreDioriteSpessartine +oreAndesiteSpessartine +oreBlackgraniteSpessartine +oreRedgraniteSpessartine +oreMarbleSpessartine +oreBasaltSpessartine +oreSphalerite +oreNetherrackSphalerite +oreEndstoneSphalerite +oreSandSphalerite +oreRedSandSphalerite +oreGraniteSphalerite +oreDioriteSphalerite +oreAndesiteSphalerite +oreBlackgraniteSphalerite +oreRedgraniteSphalerite +oreMarbleSphalerite +oreBasaltSphalerite +oreStibnite +oreNetherrackStibnite +oreEndstoneStibnite +oreSandStibnite +oreRedSandStibnite +oreGraniteStibnite +oreDioriteStibnite +oreAndesiteStibnite +oreBlackgraniteStibnite +oreRedgraniteStibnite +oreMarbleStibnite +oreBasaltStibnite +oreTetrahedrite +oreNetherrackTetrahedrite +oreEndstoneTetrahedrite +oreSandTetrahedrite +oreRedSandTetrahedrite +oreGraniteTetrahedrite +oreDioriteTetrahedrite +oreAndesiteTetrahedrite +oreBlackgraniteTetrahedrite +oreRedgraniteTetrahedrite +oreMarbleTetrahedrite +oreBasaltTetrahedrite +oreTopaz +oreNetherrackTopaz +oreEndstoneTopaz +oreSandTopaz +oreRedSandTopaz +oreGraniteTopaz +oreDioriteTopaz +oreAndesiteTopaz +oreBlackgraniteTopaz +oreRedgraniteTopaz +oreMarbleTopaz +oreBasaltTopaz +oreTungstate +oreNetherrackTungstate +oreEndstoneTungstate +oreSandTungstate +oreRedSandTungstate +oreGraniteTungstate +oreDioriteTungstate +oreAndesiteTungstate +oreBlackgraniteTungstate +oreRedgraniteTungstate +oreMarbleTungstate +oreBasaltTungstate +oreUraninite +oreNetherrackUraninite +oreEndstoneUraninite +oreSandUraninite +oreRedSandUraninite +oreGraniteUraninite +oreDioriteUraninite +oreAndesiteUraninite +oreBlackgraniteUraninite +oreRedgraniteUraninite +oreMarbleUraninite +oreBasaltUraninite +oreWulfenite +oreNetherrackWulfenite +oreEndstoneWulfenite +oreSandWulfenite +oreRedSandWulfenite +oreGraniteWulfenite +oreDioriteWulfenite +oreAndesiteWulfenite +oreBlackgraniteWulfenite +oreRedgraniteWulfenite +oreMarbleWulfenite +oreBasaltWulfenite +oreYellowLimonite +oreNetherrackYellowLimonite +oreEndstoneYellowLimonite +oreSandYellowLimonite +oreRedSandYellowLimonite +oreGraniteYellowLimonite +oreDioriteYellowLimonite +oreAndesiteYellowLimonite +oreBlackgraniteYellowLimonite +oreRedgraniteYellowLimonite +oreMarbleYellowLimonite +oreBasaltYellowLimonite +oreNetherQuartz +oreNetherrackNetherQuartz +oreEndstoneNetherQuartz +oreSandNetherQuartz +oreRedSandNetherQuartz +oreGraniteNetherQuartz +oreDioriteNetherQuartz +oreAndesiteNetherQuartz +oreBlackgraniteNetherQuartz +oreRedgraniteNetherQuartz +oreMarbleNetherQuartz +oreBasaltNetherQuartz +oreQuartzite +oreNetherrackQuartzite +oreEndstoneQuartzite +oreSandQuartzite +oreRedSandQuartzite +oreGraniteQuartzite +oreDioriteQuartzite +oreAndesiteQuartzite +oreBlackgraniteQuartzite +oreRedgraniteQuartzite +oreMarbleQuartzite +oreBasaltQuartzite +oreGraphite +oreNetherrackGraphite +oreEndstoneGraphite +oreSandGraphite +oreRedSandGraphite +oreGraniteGraphite +oreDioriteGraphite +oreAndesiteGraphite +oreBlackgraniteGraphite +oreRedgraniteGraphite +oreMarbleGraphite +oreBasaltGraphite +oreBornite +oreNetherrackBornite +oreEndstoneBornite +oreSandBornite +oreRedSandBornite +oreGraniteBornite +oreDioriteBornite +oreAndesiteBornite +oreBlackgraniteBornite +oreRedgraniteBornite +oreMarbleBornite +oreBasaltBornite +oreChalcocite +oreNetherrackChalcocite +oreEndstoneChalcocite +oreSandChalcocite +oreRedSandChalcocite +oreGraniteChalcocite +oreDioriteChalcocite +oreAndesiteChalcocite +oreBlackgraniteChalcocite +oreRedgraniteChalcocite +oreMarbleChalcocite +oreBasaltChalcocite +oreRealgar +oreNetherrackRealgar +oreEndstoneRealgar +oreSandRealgar +oreRedSandRealgar +oreGraniteRealgar +oreDioriteRealgar +oreAndesiteRealgar +oreBlackgraniteRealgar +oreRedgraniteRealgar +oreMarbleRealgar +oreBasaltRealgar +oreBastnasite +oreNetherrackBastnasite +oreEndstoneBastnasite +oreSandBastnasite +oreRedSandBastnasite +oreGraniteBastnasite +oreDioriteBastnasite +oreAndesiteBastnasite +oreBlackgraniteBastnasite +oreRedgraniteBastnasite +oreMarbleBastnasite +oreBasaltBastnasite +orePentlandite +oreNetherrackPentlandite +oreEndstonePentlandite +oreSandPentlandite +oreRedSandPentlandite +oreGranitePentlandite +oreDioritePentlandite +oreAndesitePentlandite +oreBlackgranitePentlandite +oreRedgranitePentlandite +oreMarblePentlandite +oreBasaltPentlandite +oreSpodumene +oreNetherrackSpodumene +oreEndstoneSpodumene +oreSandSpodumene +oreRedSandSpodumene +oreGraniteSpodumene +oreDioriteSpodumene +oreAndesiteSpodumene +oreBlackgraniteSpodumene +oreRedgraniteSpodumene +oreMarbleSpodumene +oreBasaltSpodumene +oreLepidolite +oreNetherrackLepidolite +oreEndstoneLepidolite +oreSandLepidolite +oreRedSandLepidolite +oreGraniteLepidolite +oreDioriteLepidolite +oreAndesiteLepidolite +oreBlackgraniteLepidolite +oreRedgraniteLepidolite +oreMarbleLepidolite +oreBasaltLepidolite +oreGlauconiteSand +oreNetherrackGlauconiteSand +oreEndstoneGlauconiteSand +oreSandGlauconiteSand +oreRedSandGlauconiteSand +oreGraniteGlauconiteSand +oreDioriteGlauconiteSand +oreAndesiteGlauconiteSand +oreBlackgraniteGlauconiteSand +oreRedgraniteGlauconiteSand +oreMarbleGlauconiteSand +oreBasaltGlauconiteSand +oreMalachite +oreNetherrackMalachite +oreEndstoneMalachite +oreSandMalachite +oreRedSandMalachite +oreGraniteMalachite +oreDioriteMalachite +oreAndesiteMalachite +oreBlackgraniteMalachite +oreRedgraniteMalachite +oreMarbleMalachite +oreBasaltMalachite +oreMica +oreNetherrackMica +oreEndstoneMica +oreSandMica +oreRedSandMica +oreGraniteMica +oreDioriteMica +oreAndesiteMica +oreBlackgraniteMica +oreRedgraniteMica +oreMarbleMica +oreBasaltMica +oreBarite +oreNetherrackBarite +oreEndstoneBarite +oreSandBarite +oreRedSandBarite +oreGraniteBarite +oreDioriteBarite +oreAndesiteBarite +oreBlackgraniteBarite +oreRedgraniteBarite +oreMarbleBarite +oreBasaltBarite +oreAlunite +oreNetherrackAlunite +oreEndstoneAlunite +oreSandAlunite +oreRedSandAlunite +oreGraniteAlunite +oreDioriteAlunite +oreAndesiteAlunite +oreBlackgraniteAlunite +oreRedgraniteAlunite +oreMarbleAlunite +oreBasaltAlunite +oreTalc +oreNetherrackTalc +oreEndstoneTalc +oreSandTalc +oreRedSandTalc +oreGraniteTalc +oreDioriteTalc +oreAndesiteTalc +oreBlackgraniteTalc +oreRedgraniteTalc +oreMarbleTalc +oreBasaltTalc +oreSoapstone +oreNetherrackSoapstone +oreEndstoneSoapstone +oreSandSoapstone +oreRedSandSoapstone +oreGraniteSoapstone +oreDioriteSoapstone +oreAndesiteSoapstone +oreBlackgraniteSoapstone +oreRedgraniteSoapstone +oreMarbleSoapstone +oreBasaltSoapstone +oreKyanite +oreNetherrackKyanite +oreEndstoneKyanite +oreSandKyanite +oreRedSandKyanite +oreGraniteKyanite +oreDioriteKyanite +oreAndesiteKyanite +oreBlackgraniteKyanite +oreRedgraniteKyanite +oreMarbleKyanite +oreBasaltKyanite +orePyrochlore +oreNetherrackPyrochlore +oreEndstonePyrochlore +oreSandPyrochlore +oreRedSandPyrochlore +oreGranitePyrochlore +oreDioritePyrochlore +oreAndesitePyrochlore +oreBlackgranitePyrochlore +oreRedgranitePyrochlore +oreMarblePyrochlore +oreBasaltPyrochlore +oreOilsands +oreNetherrackOilsands +oreEndstoneOilsands +oreSandOilsands +oreRedSandOilsands +oreGraniteOilsands +oreDioriteOilsands +oreAndesiteOilsands +oreBlackgraniteOilsands +oreRedgraniteOilsands +oreMarbleOilsands +oreBasaltOilsands +oreOlivine +oreNetherrackOlivine +oreEndstoneOlivine +oreSandOlivine +oreRedSandOlivine +oreGraniteOlivine +oreDioriteOlivine +oreAndesiteOlivine +oreBlackgraniteOlivine +oreRedgraniteOlivine +oreMarbleOlivine +oreBasaltOlivine +oreOpal +oreNetherrackOpal +oreEndstoneOpal +oreSandOpal +oreRedSandOpal +oreGraniteOpal +oreDioriteOpal +oreAndesiteOpal +oreBlackgraniteOpal +oreRedgraniteOpal +oreMarbleOpal +oreBasaltOpal +oreAmethyst +oreNetherrackAmethyst +oreEndstoneAmethyst +oreSandAmethyst +oreRedSandAmethyst +oreGraniteAmethyst +oreDioriteAmethyst +oreAndesiteAmethyst +oreBlackgraniteAmethyst +oreRedgraniteAmethyst +oreMarbleAmethyst +oreBasaltAmethyst +oreNetherrackLapis +oreEndstoneLapis +oreSandLapis +oreRedSandLapis +oreGraniteLapis +oreDioriteLapis +oreAndesiteLapis +oreBlackgraniteLapis +oreRedgraniteLapis +oreMarbleLapis +oreBasaltLapis +oreApatite +oreNetherrackApatite +oreEndstoneApatite +oreSandApatite +oreRedSandApatite +oreGraniteApatite +oreDioriteApatite +oreAndesiteApatite +oreBlackgraniteApatite +oreRedgraniteApatite +oreMarbleApatite +oreBasaltApatite +oreTricalciumPhosphate +oreNetherrackTricalciumPhosphate +oreEndstoneTricalciumPhosphate +oreSandTricalciumPhosphate +oreRedSandTricalciumPhosphate +oreGraniteTricalciumPhosphate +oreDioriteTricalciumPhosphate +oreAndesiteTricalciumPhosphate +oreBlackgraniteTricalciumPhosphate +oreRedgraniteTricalciumPhosphate +oreMarbleTricalciumPhosphate +oreBasaltTricalciumPhosphate +oreGarnetRed +oreNetherrackGarnetRed +oreEndstoneGarnetRed +oreSandGarnetRed +oreRedSandGarnetRed +oreGraniteGarnetRed +oreDioriteGarnetRed +oreAndesiteGarnetRed +oreBlackgraniteGarnetRed +oreRedgraniteGarnetRed +oreMarbleGarnetRed +oreBasaltGarnetRed +oreGarnetYellow +oreNetherrackGarnetYellow +oreEndstoneGarnetYellow +oreSandGarnetYellow +oreRedSandGarnetYellow +oreGraniteGarnetYellow +oreDioriteGarnetYellow +oreAndesiteGarnetYellow +oreBlackgraniteGarnetYellow +oreRedgraniteGarnetYellow +oreMarbleGarnetYellow +oreBasaltGarnetYellow +oreVanadiumMagnetite +oreNetherrackVanadiumMagnetite +oreEndstoneVanadiumMagnetite +oreSandVanadiumMagnetite +oreRedSandVanadiumMagnetite +oreGraniteVanadiumMagnetite +oreDioriteVanadiumMagnetite +oreAndesiteVanadiumMagnetite +oreBlackgraniteVanadiumMagnetite +oreRedgraniteVanadiumMagnetite +oreMarbleVanadiumMagnetite +oreBasaltVanadiumMagnetite +orePollucite +oreNetherrackPollucite +oreEndstonePollucite +oreSandPollucite +oreRedSandPollucite +oreGranitePollucite +oreDioritePollucite +oreAndesitePollucite +oreBlackgranitePollucite +oreRedgranitePollucite +oreMarblePollucite +oreBasaltPollucite +oreBentonite +oreNetherrackBentonite +oreEndstoneBentonite +oreSandBentonite +oreRedSandBentonite +oreGraniteBentonite +oreDioriteBentonite +oreAndesiteBentonite +oreBlackgraniteBentonite +oreRedgraniteBentonite +oreMarbleBentonite +oreBasaltBentonite +oreFullersEarth +oreNetherrackFullersEarth +oreEndstoneFullersEarth +oreSandFullersEarth +oreRedSandFullersEarth +oreGraniteFullersEarth +oreDioriteFullersEarth +oreAndesiteFullersEarth +oreBlackgraniteFullersEarth +oreRedgraniteFullersEarth +oreMarbleFullersEarth +oreBasaltFullersEarth +orePitchblende +oreNetherrackPitchblende +oreEndstonePitchblende +oreSandPitchblende +oreRedSandPitchblende +oreGranitePitchblende +oreDioritePitchblende +oreAndesitePitchblende +oreBlackgranitePitchblende +oreRedgranitePitchblende +oreMarblePitchblende +oreBasaltPitchblende +oreMonazite +oreNetherrackMonazite +oreEndstoneMonazite +oreSandMonazite +oreRedSandMonazite +oreGraniteMonazite +oreDioriteMonazite +oreAndesiteMonazite +oreBlackgraniteMonazite +oreRedgraniteMonazite +oreMarbleMonazite +oreBasaltMonazite +oreTrona +oreNetherrackTrona +oreEndstoneTrona +oreSandTrona +oreRedSandTrona +oreGraniteTrona +oreDioriteTrona +oreAndesiteTrona +oreBlackgraniteTrona +oreRedgraniteTrona +oreMarbleTrona +oreBasaltTrona +oreGypsum +oreNetherrackGypsum +oreEndstoneGypsum +oreSandGypsum +oreRedSandGypsum +oreGraniteGypsum +oreDioriteGypsum +oreAndesiteGypsum +oreBlackgraniteGypsum +oreRedgraniteGypsum +oreMarbleGypsum +oreBasaltGypsum +oreZeolite +oreNetherrackZeolite +oreEndstoneZeolite +oreSandZeolite +oreRedSandZeolite +oreGraniteZeolite +oreDioriteZeolite +oreAndesiteZeolite +oreBlackgraniteZeolite +oreRedgraniteZeolite +oreMarbleZeolite +oreBasaltZeolite +oreNetherrackRedstone +oreEndstoneRedstone +oreSandRedstone +oreRedSandRedstone +oreGraniteRedstone +oreDioriteRedstone +oreAndesiteRedstone +oreBlackgraniteRedstone +oreRedgraniteRedstone +oreMarbleRedstone +oreBasaltRedstone +oreElectrotine +oreNetherrackElectrotine +oreEndstoneElectrotine +oreSandElectrotine +oreRedSandElectrotine +oreGraniteElectrotine +oreDioriteElectrotine +oreAndesiteElectrotine +oreBlackgraniteElectrotine +oreRedgraniteElectrotine +oreMarbleElectrotine +oreBasaltElectrotine +oreDiatomite +oreNetherrackDiatomite +oreEndstoneDiatomite +oreSandDiatomite +oreRedSandDiatomite +oreGraniteDiatomite +oreDioriteDiatomite +oreAndesiteDiatomite +oreBlackgraniteDiatomite +oreRedgraniteDiatomite +oreMarbleDiatomite +oreBasaltDiatomite +oreGraniticMineralSand +oreNetherrackGraniticMineralSand +oreEndstoneGraniticMineralSand +oreSandGraniticMineralSand +oreRedSandGraniticMineralSand +oreGraniteGraniticMineralSand +oreDioriteGraniticMineralSand +oreAndesiteGraniticMineralSand +oreBlackgraniteGraniticMineralSand +oreRedgraniteGraniticMineralSand +oreMarbleGraniticMineralSand +oreBasaltGraniticMineralSand +oreGarnetSand +oreNetherrackGarnetSand +oreEndstoneGarnetSand +oreSandGarnetSand +oreRedSandGarnetSand +oreGraniteGarnetSand +oreDioriteGarnetSand +oreAndesiteGarnetSand +oreBlackgraniteGarnetSand +oreRedgraniteGarnetSand +oreMarbleGarnetSand +oreBasaltGarnetSand +oreBasalticMineralSand +oreNetherrackBasalticMineralSand +oreEndstoneBasalticMineralSand +oreSandBasalticMineralSand +oreRedSandBasalticMineralSand +oreGraniteBasalticMineralSand +oreDioriteBasalticMineralSand +oreAndesiteBasalticMineralSand +oreBlackgraniteBasalticMineralSand +oreRedgraniteBasalticMineralSand +oreMarbleBasalticMineralSand +oreBasaltBasalticMineralSand +wireGtSingleConductiveIron +wireGtSingleDraconicSuperconductor +wireGtSingleDraconium +wireGtSingleEndSteel +wireGtSingleEnderium +wireGtSingleEnergeticAlloy +wireGtSingleLumium +wireGtSingleMicroversium +wireGtSingleOmnium +wireGtSinglePulsatingIron +wireGtSingleSignalum +wireGtSingleVibrantAlloy +wireGtDoubleConductiveIron +wireGtDoubleDraconicSuperconductor +wireGtDoubleDraconium +wireGtDoubleEndSteel +wireGtDoubleEnderium +wireGtDoubleEnergeticAlloy +wireGtDoubleLumium +wireGtDoubleMicroversium +wireGtDoubleOmnium +wireGtDoublePulsatingIron +wireGtDoubleSignalum +wireGtDoubleVibrantAlloy +wireGtQuadrupleConductiveIron +wireGtQuadrupleDraconicSuperconductor +wireGtQuadrupleDraconium +wireGtQuadrupleEndSteel +wireGtQuadrupleEnderium +wireGtQuadrupleEnergeticAlloy +wireGtQuadrupleLumium +wireGtQuadrupleMicroversium +wireGtQuadrupleOmnium +wireGtQuadruplePulsatingIron +wireGtQuadrupleSignalum +wireGtQuadrupleVibrantAlloy +wireGtOctalConductiveIron +wireGtOctalDraconicSuperconductor +wireGtOctalDraconium +wireGtOctalEndSteel +wireGtOctalEnderium +wireGtOctalEnergeticAlloy +wireGtOctalLumium +wireGtOctalMicroversium +wireGtOctalOmnium +wireGtOctalPulsatingIron +wireGtOctalSignalum +wireGtOctalVibrantAlloy +wireGtHexConductiveIron +wireGtHexDraconicSuperconductor +wireGtHexDraconium +wireGtHexEndSteel +wireGtHexEnderium +wireGtHexEnergeticAlloy +wireGtHexLumium +wireGtHexMicroversium +wireGtHexOmnium +wireGtHexPulsatingIron +wireGtHexSignalum +wireGtHexVibrantAlloy +cableGtSingleMicroversium +cableGtDoubleMicroversium +cableGtQuadrupleMicroversium +cableGtOctalMicroversium +cableGtHexMicroversium +pipeTinyFluidLumium +pipeSmallFluidLumium +pipeNormalFluidLumium +pipeLargeFluidLumium +pipeHugeFluidLumium +pipeQuadrupleFluidLumium +pipeNonupleFluidLumium +pipeSmallItemSignalum +pipeNormalItemSignalum +pipeLargeItemSignalum +pipeHugeItemSignalum +pipeSmallRestrictiveSignalum +pipeNormalRestrictiveSignalum +pipeLargeRestrictiveSignalum +pipeHugeRestrictiveSignalum +wireGtSingleAluminium +wireGtSingleAnnealedCopper +wireGtSingleBlackSteel +wireGtSingleBlueAlloy +wireGtSingleCobalt +wireGtSingleCopper +wireGtSingleCupronickel +wireGtSingleElectrum +wireGtSingleEnrichedNaquadahTriniumEuropiumDuranide +wireGtSingleEuropium +wireGtSingleGold +wireGtSingleGraphene +wireGtSingleHssg +wireGtSingleIndiumTinBariumTitaniumCuprate +wireGtSingleIron +wireGtSingleKanthal +wireGtSingleLead +wireGtSingleMagnesiumDiboride +wireGtSingleManganesePhosphide +wireGtSingleMercuryBariumCalciumCuprate +wireGtSingleNaquadah +wireGtSingleNaquadahAlloy +wireGtSingleNichrome +wireGtSingleNickel +wireGtSingleNiobiumNitride +wireGtSingleNiobiumTitanium +wireGtSingleOsmium +wireGtSinglePlatinum +wireGtSingleRedAlloy +wireGtSingleRtmAlloy +wireGtSingleRutheniumTriniumAmericiumNeutronate +wireGtSingleSamariumIronArsenicOxide +wireGtSingleSilver +wireGtSingleSteel +wireGtSingleTin +wireGtSingleTrinium +wireGtSingleTritanium +wireGtSingleTungsten +wireGtSingleTungstenSteel +wireGtSingleUraniumRhodiumDinaquadide +wireGtSingleUraniumTriplatinum +wireGtSingleVanadiumGallium +wireGtSingleYttriumBariumCuprate +wireGtDoubleAluminium +wireGtDoubleAnnealedCopper +wireGtDoubleBlackSteel +wireGtDoubleBlueAlloy +wireGtDoubleCobalt +wireGtDoubleCopper +wireGtDoubleCupronickel +wireGtDoubleElectrum +wireGtDoubleEnrichedNaquadahTriniumEuropiumDuranide +wireGtDoubleEuropium +wireGtDoubleGold +wireGtDoubleGraphene +wireGtDoubleHssg +wireGtDoubleIndiumTinBariumTitaniumCuprate +wireGtDoubleIron +wireGtDoubleKanthal +wireGtDoubleLead +wireGtDoubleMagnesiumDiboride +wireGtDoubleManganesePhosphide +wireGtDoubleMercuryBariumCalciumCuprate +wireGtDoubleNaquadah +wireGtDoubleNaquadahAlloy +wireGtDoubleNichrome +wireGtDoubleNickel +wireGtDoubleNiobiumNitride +wireGtDoubleNiobiumTitanium +wireGtDoubleOsmium +wireGtDoublePlatinum +wireGtDoubleRedAlloy +wireGtDoubleRtmAlloy +wireGtDoubleRutheniumTriniumAmericiumNeutronate +wireGtDoubleSamariumIronArsenicOxide +wireGtDoubleSilver +wireGtDoubleSteel +wireGtDoubleTin +wireGtDoubleTrinium +wireGtDoubleTritanium +wireGtDoubleTungsten +wireGtDoubleTungstenSteel +wireGtDoubleUraniumRhodiumDinaquadide +wireGtDoubleUraniumTriplatinum +wireGtDoubleVanadiumGallium +wireGtDoubleYttriumBariumCuprate +wireGtQuadrupleAluminium +wireGtQuadrupleAnnealedCopper +wireGtQuadrupleBlackSteel +wireGtQuadrupleBlueAlloy +wireGtQuadrupleCobalt +wireGtQuadrupleCopper +wireGtQuadrupleCupronickel +wireGtQuadrupleElectrum +wireGtQuadrupleEnrichedNaquadahTriniumEuropiumDuranide +wireGtQuadrupleEuropium +wireGtQuadrupleGold +wireGtQuadrupleGraphene +wireGtQuadrupleHssg +wireGtQuadrupleIndiumTinBariumTitaniumCuprate +wireGtQuadrupleIron +wireGtQuadrupleKanthal +wireGtQuadrupleLead +wireGtQuadrupleMagnesiumDiboride +wireGtQuadrupleManganesePhosphide +wireGtQuadrupleMercuryBariumCalciumCuprate +wireGtQuadrupleNaquadah +wireGtQuadrupleNaquadahAlloy +wireGtQuadrupleNichrome +wireGtQuadrupleNickel +wireGtQuadrupleNiobiumNitride +wireGtQuadrupleNiobiumTitanium +wireGtQuadrupleOsmium +wireGtQuadruplePlatinum +wireGtQuadrupleRedAlloy +wireGtQuadrupleRtmAlloy +wireGtQuadrupleRutheniumTriniumAmericiumNeutronate +wireGtQuadrupleSamariumIronArsenicOxide +wireGtQuadrupleSilver +wireGtQuadrupleSteel +wireGtQuadrupleTin +wireGtQuadrupleTrinium +wireGtQuadrupleTritanium +wireGtQuadrupleTungsten +wireGtQuadrupleTungstenSteel +wireGtQuadrupleUraniumRhodiumDinaquadide +wireGtQuadrupleUraniumTriplatinum +wireGtQuadrupleVanadiumGallium +wireGtQuadrupleYttriumBariumCuprate +wireGtOctalAluminium +wireGtOctalAnnealedCopper +wireGtOctalBlackSteel +wireGtOctalBlueAlloy +wireGtOctalCobalt +wireGtOctalCopper +wireGtOctalCupronickel +wireGtOctalElectrum +wireGtOctalEnrichedNaquadahTriniumEuropiumDuranide +wireGtOctalEuropium +wireGtOctalGold +wireGtOctalGraphene +wireGtOctalHssg +wireGtOctalIndiumTinBariumTitaniumCuprate +wireGtOctalIron +wireGtOctalKanthal +wireGtOctalLead +wireGtOctalMagnesiumDiboride +wireGtOctalManganesePhosphide +wireGtOctalMercuryBariumCalciumCuprate +wireGtOctalNaquadah +wireGtOctalNaquadahAlloy +wireGtOctalNichrome +wireGtOctalNickel +wireGtOctalNiobiumNitride +wireGtOctalNiobiumTitanium +wireGtOctalOsmium +wireGtOctalPlatinum +wireGtOctalRedAlloy +wireGtOctalRtmAlloy +wireGtOctalRutheniumTriniumAmericiumNeutronate +wireGtOctalSamariumIronArsenicOxide +wireGtOctalSilver +wireGtOctalSteel +wireGtOctalTin +wireGtOctalTrinium +wireGtOctalTritanium +wireGtOctalTungsten +wireGtOctalTungstenSteel +wireGtOctalUraniumRhodiumDinaquadide +wireGtOctalUraniumTriplatinum +wireGtOctalVanadiumGallium +wireGtOctalYttriumBariumCuprate +wireGtHexAluminium +wireGtHexAnnealedCopper +wireGtHexBlackSteel +wireGtHexBlueAlloy +wireGtHexCobalt +wireGtHexCopper +wireGtHexCupronickel +wireGtHexElectrum +wireGtHexEnrichedNaquadahTriniumEuropiumDuranide +wireGtHexEuropium +wireGtHexGold +wireGtHexGraphene +wireGtHexHssg +wireGtHexIndiumTinBariumTitaniumCuprate +wireGtHexIron +wireGtHexKanthal +wireGtHexLead +wireGtHexMagnesiumDiboride +wireGtHexManganesePhosphide +wireGtHexMercuryBariumCalciumCuprate +wireGtHexNaquadah +wireGtHexNaquadahAlloy +wireGtHexNichrome +wireGtHexNickel +wireGtHexNiobiumNitride +wireGtHexNiobiumTitanium +wireGtHexOsmium +wireGtHexPlatinum +wireGtHexRedAlloy +wireGtHexRtmAlloy +wireGtHexRutheniumTriniumAmericiumNeutronate +wireGtHexSamariumIronArsenicOxide +wireGtHexSilver +wireGtHexSteel +wireGtHexTin +wireGtHexTrinium +wireGtHexTritanium +wireGtHexTungsten +wireGtHexTungstenSteel +wireGtHexUraniumRhodiumDinaquadide +wireGtHexUraniumTriplatinum +wireGtHexVanadiumGallium +wireGtHexYttriumBariumCuprate +cableGtSingleAluminium +cableGtSingleAnnealedCopper +cableGtSingleBlackSteel +cableGtSingleBlueAlloy +cableGtSingleCobalt +cableGtSingleCopper +cableGtSingleCupronickel +cableGtSingleElectrum +cableGtSingleEuropium +cableGtSingleGold +cableGtSingleGraphene +cableGtSingleHssg +cableGtSingleIron +cableGtSingleKanthal +cableGtSingleLead +cableGtSingleNaquadah +cableGtSingleNaquadahAlloy +cableGtSingleNichrome +cableGtSingleNickel +cableGtSingleNiobiumNitride +cableGtSingleNiobiumTitanium +cableGtSingleOsmium +cableGtSinglePlatinum +cableGtSingleRedAlloy +cableGtSingleRtmAlloy +cableGtSingleSilver +cableGtSingleSteel +cableGtSingleTin +cableGtSingleTrinium +cableGtSingleTritanium +cableGtSingleTungsten +cableGtSingleTungstenSteel +cableGtSingleVanadiumGallium +cableGtSingleYttriumBariumCuprate +cableGtDoubleAluminium +cableGtDoubleAnnealedCopper +cableGtDoubleBlackSteel +cableGtDoubleBlueAlloy +cableGtDoubleCobalt +cableGtDoubleCopper +cableGtDoubleCupronickel +cableGtDoubleElectrum +cableGtDoubleEuropium +cableGtDoubleGold +cableGtDoubleGraphene +cableGtDoubleHssg +cableGtDoubleIron +cableGtDoubleKanthal +cableGtDoubleLead +cableGtDoubleNaquadah +cableGtDoubleNaquadahAlloy +cableGtDoubleNichrome +cableGtDoubleNickel +cableGtDoubleNiobiumNitride +cableGtDoubleNiobiumTitanium +cableGtDoubleOsmium +cableGtDoublePlatinum +cableGtDoubleRedAlloy +cableGtDoubleRtmAlloy +cableGtDoubleSilver +cableGtDoubleSteel +cableGtDoubleTin +cableGtDoubleTrinium +cableGtDoubleTritanium +cableGtDoubleTungsten +cableGtDoubleTungstenSteel +cableGtDoubleVanadiumGallium +cableGtDoubleYttriumBariumCuprate +cableGtQuadrupleAluminium +cableGtQuadrupleAnnealedCopper +cableGtQuadrupleBlackSteel +cableGtQuadrupleBlueAlloy +cableGtQuadrupleCobalt +cableGtQuadrupleCopper +cableGtQuadrupleCupronickel +cableGtQuadrupleElectrum +cableGtQuadrupleEuropium +cableGtQuadrupleGold +cableGtQuadrupleGraphene +cableGtQuadrupleHssg +cableGtQuadrupleIron +cableGtQuadrupleKanthal +cableGtQuadrupleLead +cableGtQuadrupleNaquadah +cableGtQuadrupleNaquadahAlloy +cableGtQuadrupleNichrome +cableGtQuadrupleNickel +cableGtQuadrupleNiobiumNitride +cableGtQuadrupleNiobiumTitanium +cableGtQuadrupleOsmium +cableGtQuadruplePlatinum +cableGtQuadrupleRedAlloy +cableGtQuadrupleRtmAlloy +cableGtQuadrupleSilver +cableGtQuadrupleSteel +cableGtQuadrupleTin +cableGtQuadrupleTrinium +cableGtQuadrupleTritanium +cableGtQuadrupleTungsten +cableGtQuadrupleTungstenSteel +cableGtQuadrupleVanadiumGallium +cableGtQuadrupleYttriumBariumCuprate +cableGtOctalAluminium +cableGtOctalAnnealedCopper +cableGtOctalBlackSteel +cableGtOctalBlueAlloy +cableGtOctalCobalt +cableGtOctalCopper +cableGtOctalCupronickel +cableGtOctalElectrum +cableGtOctalEuropium +cableGtOctalGold +cableGtOctalGraphene +cableGtOctalHssg +cableGtOctalIron +cableGtOctalKanthal +cableGtOctalLead +cableGtOctalNaquadah +cableGtOctalNaquadahAlloy +cableGtOctalNichrome +cableGtOctalNickel +cableGtOctalNiobiumNitride +cableGtOctalNiobiumTitanium +cableGtOctalOsmium +cableGtOctalPlatinum +cableGtOctalRedAlloy +cableGtOctalRtmAlloy +cableGtOctalSilver +cableGtOctalSteel +cableGtOctalTin +cableGtOctalTrinium +cableGtOctalTritanium +cableGtOctalTungsten +cableGtOctalTungstenSteel +cableGtOctalVanadiumGallium +cableGtOctalYttriumBariumCuprate +cableGtHexAluminium +cableGtHexAnnealedCopper +cableGtHexBlackSteel +cableGtHexBlueAlloy +cableGtHexCobalt +cableGtHexCopper +cableGtHexCupronickel +cableGtHexElectrum +cableGtHexEuropium +cableGtHexGold +cableGtHexGraphene +cableGtHexHssg +cableGtHexIron +cableGtHexKanthal +cableGtHexLead +cableGtHexNaquadah +cableGtHexNaquadahAlloy +cableGtHexNichrome +cableGtHexNickel +cableGtHexNiobiumNitride +cableGtHexNiobiumTitanium +cableGtHexOsmium +cableGtHexPlatinum +cableGtHexRedAlloy +cableGtHexRtmAlloy +cableGtHexSilver +cableGtHexSteel +cableGtHexTin +cableGtHexTrinium +cableGtHexTritanium +cableGtHexTungsten +cableGtHexTungstenSteel +cableGtHexVanadiumGallium +cableGtHexYttriumBariumCuprate +pipeTinyFluidAluminium +pipeTinyFluidBronze +pipeTinyFluidChrome +pipeTinyFluidCopper +pipeTinyFluidDuranium +pipeTinyFluidEuropium +pipeTinyFluidGold +pipeTinyFluidIridium +pipeTinyFluidLead +pipeTinyFluidNaquadah +pipeTinyFluidNeutronium +pipeTinyFluidNiobiumTitanium +pipeTinyFluidPlastic +pipeTinyFluidPolybenzimidazole +pipeTinyFluidPolytetrafluoroethylene +pipeTinyFluidPotin +pipeTinyFluidStainlessSteel +pipeTinyFluidSteel +pipeTinyFluidTinAlloy +pipeTinyFluidTitanium +pipeTinyFluidTungsten +pipeTinyFluidTungstenCarbide +pipeTinyFluidTungstenSteel +pipeTinyFluidVanadiumSteel +pipeSmallFluidAluminium +pipeSmallFluidBronze +pipeSmallFluidChrome +pipeSmallFluidCopper +pipeSmallFluidDuranium +pipeSmallFluidEuropium +pipeSmallFluidGold +pipeSmallFluidIridium +pipeSmallFluidLead +pipeSmallFluidNaquadah +pipeSmallFluidNeutronium +pipeSmallFluidNiobiumTitanium +pipeSmallFluidPlastic +pipeSmallFluidPolybenzimidazole +pipeSmallFluidPolytetrafluoroethylene +pipeSmallFluidPotin +pipeSmallFluidStainlessSteel +pipeSmallFluidSteel +pipeSmallFluidTinAlloy +pipeSmallFluidTitanium +pipeSmallFluidTreatedWood +pipeSmallFluidTungsten +pipeSmallFluidTungstenCarbide +pipeSmallFluidTungstenSteel +pipeSmallFluidVanadiumSteel +pipeSmallFluidWood +pipeNormalFluidAluminium +pipeNormalFluidBronze +pipeNormalFluidChrome +pipeNormalFluidCopper +pipeNormalFluidDuranium +pipeNormalFluidEuropium +pipeNormalFluidGold +pipeNormalFluidIridium +pipeNormalFluidLead +pipeNormalFluidNaquadah +pipeNormalFluidNeutronium +pipeNormalFluidNiobiumTitanium +pipeNormalFluidPlastic +pipeNormalFluidPolybenzimidazole +pipeNormalFluidPolytetrafluoroethylene +pipeNormalFluidPotin +pipeNormalFluidStainlessSteel +pipeNormalFluidSteel +pipeNormalFluidTinAlloy +pipeNormalFluidTitanium +pipeNormalFluidTreatedWood +pipeNormalFluidTungsten +pipeNormalFluidTungstenCarbide +pipeNormalFluidTungstenSteel +pipeNormalFluidVanadiumSteel +pipeNormalFluidWood +pipeLargeFluidAluminium +pipeLargeFluidBronze +pipeLargeFluidChrome +pipeLargeFluidCopper +pipeLargeFluidDuranium +pipeLargeFluidEuropium +pipeLargeFluidGold +pipeLargeFluidIridium +pipeLargeFluidLead +pipeLargeFluidNaquadah +pipeLargeFluidNeutronium +pipeLargeFluidNiobiumTitanium +pipeLargeFluidPlastic +pipeLargeFluidPolybenzimidazole +pipeLargeFluidPolytetrafluoroethylene +pipeLargeFluidPotin +pipeLargeFluidStainlessSteel +pipeLargeFluidSteel +pipeLargeFluidTinAlloy +pipeLargeFluidTitanium +pipeLargeFluidTreatedWood +pipeLargeFluidTungsten +pipeLargeFluidTungstenCarbide +pipeLargeFluidTungstenSteel +pipeLargeFluidVanadiumSteel +pipeLargeFluidWood +pipeHugeFluidAluminium +pipeHugeFluidBronze +pipeHugeFluidChrome +pipeHugeFluidCopper +pipeHugeFluidDuranium +pipeHugeFluidEuropium +pipeHugeFluidGold +pipeHugeFluidIridium +pipeHugeFluidLead +pipeHugeFluidNaquadah +pipeHugeFluidNeutronium +pipeHugeFluidNiobiumTitanium +pipeHugeFluidPlastic +pipeHugeFluidPolybenzimidazole +pipeHugeFluidPolytetrafluoroethylene +pipeHugeFluidPotin +pipeHugeFluidStainlessSteel +pipeHugeFluidSteel +pipeHugeFluidTinAlloy +pipeHugeFluidTitanium +pipeHugeFluidTungsten +pipeHugeFluidTungstenCarbide +pipeHugeFluidTungstenSteel +pipeHugeFluidVanadiumSteel +pipeQuadrupleFluidAluminium +pipeQuadrupleFluidBronze +pipeQuadrupleFluidChrome +pipeQuadrupleFluidCopper +pipeQuadrupleFluidDuranium +pipeQuadrupleFluidEuropium +pipeQuadrupleFluidGold +pipeQuadrupleFluidIridium +pipeQuadrupleFluidLead +pipeQuadrupleFluidNaquadah +pipeQuadrupleFluidNeutronium +pipeQuadrupleFluidNiobiumTitanium +pipeQuadrupleFluidPlastic +pipeQuadrupleFluidPolybenzimidazole +pipeQuadrupleFluidPolytetrafluoroethylene +pipeQuadrupleFluidPotin +pipeQuadrupleFluidStainlessSteel +pipeQuadrupleFluidSteel +pipeQuadrupleFluidTinAlloy +pipeQuadrupleFluidTitanium +pipeQuadrupleFluidTungsten +pipeQuadrupleFluidTungstenCarbide +pipeQuadrupleFluidTungstenSteel +pipeQuadrupleFluidVanadiumSteel +pipeNonupleFluidAluminium +pipeNonupleFluidBronze +pipeNonupleFluidChrome +pipeNonupleFluidCopper +pipeNonupleFluidDuranium +pipeNonupleFluidEuropium +pipeNonupleFluidGold +pipeNonupleFluidIridium +pipeNonupleFluidLead +pipeNonupleFluidNaquadah +pipeNonupleFluidNeutronium +pipeNonupleFluidNiobiumTitanium +pipeNonupleFluidPlastic +pipeNonupleFluidPolybenzimidazole +pipeNonupleFluidPolytetrafluoroethylene +pipeNonupleFluidPotin +pipeNonupleFluidStainlessSteel +pipeNonupleFluidSteel +pipeNonupleFluidTinAlloy +pipeNonupleFluidTitanium +pipeNonupleFluidTungsten +pipeNonupleFluidTungstenCarbide +pipeNonupleFluidTungstenSteel +pipeNonupleFluidVanadiumSteel +pipeSmallItemMagnalium +pipeSmallItemOsmiridium +pipeSmallItemCobaltBrass +pipeSmallItemTin +pipeSmallItemElectrum +pipeSmallItemSterlingSilver +pipeSmallItemCupronickel +pipeSmallItemAmericium +pipeSmallItemPolyvinylChloride +pipeSmallItemCobalt +pipeSmallItemUltimet +pipeSmallItemPlatinum +pipeSmallItemBrass +pipeSmallItemNickel +pipeSmallItemRoseGold +pipeSmallItemOsmium +pipeSmallItemBlackBronze +pipeNormalItemMagnalium +pipeNormalItemOsmiridium +pipeNormalItemCobaltBrass +pipeNormalItemTin +pipeNormalItemElectrum +pipeNormalItemSterlingSilver +pipeNormalItemCupronickel +pipeNormalItemAmericium +pipeNormalItemPolyvinylChloride +pipeNormalItemCobalt +pipeNormalItemUltimet +pipeNormalItemPlatinum +pipeNormalItemBrass +pipeNormalItemNickel +pipeNormalItemRoseGold +pipeNormalItemOsmium +pipeNormalItemBlackBronze +pipeLargeItemMagnalium +pipeLargeItemOsmiridium +pipeLargeItemCobaltBrass +pipeLargeItemTin +pipeLargeItemElectrum +pipeLargeItemSterlingSilver +pipeLargeItemCupronickel +pipeLargeItemAmericium +pipeLargeItemPolyvinylChloride +pipeLargeItemCobalt +pipeLargeItemUltimet +pipeLargeItemPlatinum +pipeLargeItemBrass +pipeLargeItemNickel +pipeLargeItemRoseGold +pipeLargeItemOsmium +pipeLargeItemBlackBronze +pipeHugeItemMagnalium +pipeHugeItemOsmiridium +pipeHugeItemCobaltBrass +pipeHugeItemTin +pipeHugeItemElectrum +pipeHugeItemSterlingSilver +pipeHugeItemCupronickel +pipeHugeItemAmericium +pipeHugeItemPolyvinylChloride +pipeHugeItemCobalt +pipeHugeItemUltimet +pipeHugeItemPlatinum +pipeHugeItemBrass +pipeHugeItemNickel +pipeHugeItemRoseGold +pipeHugeItemOsmium +pipeHugeItemBlackBronze +pipeSmallRestrictiveMagnalium +pipeSmallRestrictiveOsmiridium +pipeSmallRestrictiveCobaltBrass +pipeSmallRestrictiveTin +pipeSmallRestrictiveElectrum +pipeSmallRestrictiveSterlingSilver +pipeSmallRestrictiveCupronickel +pipeSmallRestrictiveAmericium +pipeSmallRestrictivePolyvinylChloride +pipeSmallRestrictiveCobalt +pipeSmallRestrictiveUltimet +pipeSmallRestrictivePlatinum +pipeSmallRestrictiveBrass +pipeSmallRestrictiveNickel +pipeSmallRestrictiveRoseGold +pipeSmallRestrictiveOsmium +pipeSmallRestrictiveBlackBronze +pipeNormalRestrictiveMagnalium +pipeNormalRestrictiveOsmiridium +pipeNormalRestrictiveCobaltBrass +pipeNormalRestrictiveTin +pipeNormalRestrictiveElectrum +pipeNormalRestrictiveSterlingSilver +pipeNormalRestrictiveCupronickel +pipeNormalRestrictiveAmericium +pipeNormalRestrictivePolyvinylChloride +pipeNormalRestrictiveCobalt +pipeNormalRestrictiveUltimet +pipeNormalRestrictivePlatinum +pipeNormalRestrictiveBrass +pipeNormalRestrictiveNickel +pipeNormalRestrictiveRoseGold +pipeNormalRestrictiveOsmium +pipeNormalRestrictiveBlackBronze +pipeLargeRestrictiveMagnalium +pipeLargeRestrictiveOsmiridium +pipeLargeRestrictiveCobaltBrass +pipeLargeRestrictiveTin +pipeLargeRestrictiveElectrum +pipeLargeRestrictiveSterlingSilver +pipeLargeRestrictiveCupronickel +pipeLargeRestrictiveAmericium +pipeLargeRestrictivePolyvinylChloride +pipeLargeRestrictiveCobalt +pipeLargeRestrictiveUltimet +pipeLargeRestrictivePlatinum +pipeLargeRestrictiveBrass +pipeLargeRestrictiveNickel +pipeLargeRestrictiveRoseGold +pipeLargeRestrictiveOsmium +pipeLargeRestrictiveBlackBronze +pipeHugeRestrictiveMagnalium +pipeHugeRestrictiveOsmiridium +pipeHugeRestrictiveCobaltBrass +pipeHugeRestrictiveTin +pipeHugeRestrictiveElectrum +pipeHugeRestrictiveSterlingSilver +pipeHugeRestrictiveCupronickel +pipeHugeRestrictiveAmericium +pipeHugeRestrictivePolyvinylChloride +pipeHugeRestrictiveCobalt +pipeHugeRestrictiveUltimet +pipeHugeRestrictivePlatinum +pipeHugeRestrictiveBrass +pipeHugeRestrictiveNickel +pipeHugeRestrictiveRoseGold +pipeHugeRestrictiveOsmium +pipeHugeRestrictiveBlackBronze +blockClay +blockBrick +ingotClay +gemFlint +plankTreatedWood +craftingLensWhite +gemEnderEye +gemEnderPearl +gemCoal +gemCharcoal +gemNetherQuartz +gemNetherStar +platePaper +dustSugar +dustGunpowder +dustBone +stickBone +stickBlaze +blockNetherQuartz +blockBone +blockObsidian +stoneGraniteBlack +stoneGraniteRed +craftingAnvil +stoneObsidian +stoneMossy +stoneCobble +stoneSmooth +stoneBricks +stoneCracked +stoneChiseled +stoneNetherrack +stoneEndstone +craftingRedstoneTorch +craftingPiston +craftingFurnace +craftingFeather +itemWheat +paperEmpty +paperMap +bookEmpty +bookWritable +bookWritten +bookEnchanted +craftingBook +doorTreatedWood +slabTreatedWood +fenceTreatedWood +fenceGateTreatedWood +stairTreatedWood +stoneSoapstone +stoneRedrock +dustSand +cropCactus +cropMushroomRed +cropMushroomBrown +particleCustomizer +ingot_dark_soularium +h +ingotMagnesium +blockMagnesium +ingotLithium +ingotBoron +blockNetherstar +blockSkystone +dustMoon +crystalDilithium +questbookBrewery +questbookDistillery +questbookPolarizer +questbookAirCollector +questbookCanning +questbookSifter +questbookLvCef +questbookLvBatteryBuffer +questbookMvCef +questbookParallelControlHatch +questbookPowerUnit +questbookMacerator +questbookWirelessTerminals +questbookChisels +questbookScanner +questbookWA +questbookLathe +questbookAutoclave +questbookTanks +questbookNeptunium +questbookPlutonium +questbookAmericium +questbookCurium +questbookBerkelium +questbookCalifornium +questbookItemPipes +dustPulsating +meshPulsating +gemFluix +wrenches +denseOreRedstone +denseOreDiamond +denseOreIron +denseOreLapis +denseOreEmerald +denseOreMagma +denseOreOilsands +denseOreGold +denseOreCoal +barsIron +blockHopper +itemCoal +itemCharcoal +pearlEnderEye +itemBlazeRod +itemBlazePowder +itemClay +itemFlint +itemGhastTear +itemLeather +blockSlimeCongealed +ingotElectrical +gemLavaCrystal +ingotLavaCrystal +gemChargedLavaCrystal +ingotChargedLavaCrystal +gemInfusedLavaCrystal +ingotInfusedLavaCrystal +oreLavaCrystal +blockLavaCrystal +blockInfusedLavaCrystal +blockCompressedLavaCrystal +blockCompressedInfusedLavaCrystal +blockElectrical +blockCompressedObsidian +blockLavaInfusedObsidian +blockInfusedObsidian +apWorkbench +workbenchTier1 +workbenchTierOne +apHighTechBench +workbenchTier2 +workbenchTierTwo +apUltiTechBench +workbenchTier3 +workbenchTierThree +apChampionBench +workbenchTier4 +workbenchTierFour +stonebrickWhite +stonebrickWhiteCorner +stonebrickWhiteTower +stonebrickWhiteWall +stonebrickRed +stonebrickRedCorner +stonebrickRedTower +stonebrickRedWall +stonebrickBlack +stonebrickBlackCorner +stonebrickBlackTower +stonebrickBlackWall +stonebrickBlue +stonebrickBlueCorner +stonebrickBlueTower +stonebrickBlueWall +stonebrickGreen +stonebrickGreenCorner +stonebrickGreenTower +stonebrickGreenWall +stonebrickYellow +stonebrickYellowCorner +stonebrickYellowTower +stonebrickYellowWall +stonebrickPurple +stonebrickPurpleCorner +stonebrickPurpleTower +stonebrickPurpleWall +chainmail +scaleGuardian +guardianScale +witherBone +scaleEnderDragon +enderDragonScale +dragonScale +materialTheUltimate +materialUltimate +itemArrow +arrow +stonebrick +hopper +blockMagma +blockNetherWart +shulkerShell +shulkerBox +seed +rail +blockWither +ingotEnderiumBase +compressed1xDust +compressed2xDust +compressed1xEndStone +cropBarley +seedBarley +oreNetherIron +denseoreIron +oreNetherGold +denseoreGold +oreNetherCopper +denseoreCopper +oreNetherTin +denseoreTin +oreNetherLead +denseoreLead +oreNetherSilver +denseoreSilver +oreNetherNickel +denseoreNickel +oreNetherAluminium +denseoreAluminium +oreNetherAluminum +denseoreAluminum +oreNaturalAluminum +dustNaturalAluminum +oreNetherNaturalAluminum +denseoreNaturalAluminum +ingotNaturalAluminum +blockNaturalAluminum +oreNetherCoal +denseoreCoal +oreNetherRedstone +denseoreRedstone +oreNetherDiamond +denseoreDiamond +oreNetherEmerald +denseoreEmerald +oreNetherLapis +denseoreLapis +denseoreQuartz +oreNetherApatite +denseoreApatite +oreNetherCertusQuartz +denseoreCertusQuartz +oreChargedCertusQuartz +oreNetherChargedCertusQuartz +denseoreChargedCertusQuartz +oreNetherSulfur +denseoreSulfur +oreNetherSaltpeter +denseoreSaltpeter +oreNetherRuby +orePeridot +oreNetherPeridot +oreNetherTopaz +oreTanzanite +oreNetherTanzanite +oreNetherMalachite +oreNetherSapphire +oreAmber +oreNetherAmber +oreNetherAmethyst +oreManganese +oreNetherManganese +denseoreManganese +oreZinc +oreNetherZinc +denseoreZinc +oreNetherPlatinum +denseorePlatinum +oreIgnatius +dustIgnatius +oreNetherIgnatius +denseoreIgnatius +ingotIgnatius +blockIgnatius +oreShadowIron +dustShadowIron +oreNetherShadowIron +denseoreShadowIron +ingotShadowIron +blockShadowIron +oreLemurite +dustLemurite +oreNetherLemurite +denseoreLemurite +ingotLemurite +blockLemurite +oreMidasium +dustMidasium +oreNetherMidasium +denseoreMidasium +ingotMidasium +blockMidasium +oreVyroxeres +dustVyroxeres +oreNetherVyroxeres +denseoreVyroxeres +ingotVyroxeres +blockVyroxeres +oreCeruclase +dustCeruclase +oreNetherCeruclase +denseoreCeruclase +ingotCeruclase +blockCeruclase +oreKalendrite +dustKalendrite +oreNetherKalendrite +denseoreKalendrite +ingotKalendrite +blockKalendrite +oreVulcanite +dustVulcanite +oreNetherVulcanite +denseoreVulcanite +ingotVulcanite +blockVulcanite +oreSanguinite +dustSanguinite +oreNetherSanguinite +denseoreSanguinite +ingotSanguinite +blockSanguinite +orePrometheum +dustPrometheum +oreNetherPrometheum +denseorePrometheum +ingotPrometheum +blockPrometheum +oreDeepIron +dustDeepIron +oreNetherDeepIron +denseoreDeepIron +ingotDeepIron +blockDeepIron +oreInfuscolium +dustInfuscolium +oreNetherInfuscolium +denseoreInfuscolium +ingotInfuscolium +blockInfuscolium +oreOureclase +dustOureclase +oreNetherOureclase +denseoreOureclase +ingotOureclase +blockOureclase +oreAstralSilver +dustAstralSilver +oreNetherAstralSilver +denseoreAstralSilver +ingotAstralSilver +blockAstralSilver +oreCarmot +dustCarmot +oreNetherCarmot +denseoreCarmot +ingotCarmot +blockCarmot +oreNetherMithril +denseoreMithril +oreRubracium +dustRubracium +oreNetherRubracium +denseoreRubracium +ingotRubracium +blockRubracium +oreOrichalcum +dustOrichalcum +oreNetherOrichalcum +denseoreOrichalcum +ingotOrichalcum +blockOrichalcum +oreAdamantine +dustAdamantine +oreNetherAdamantine +denseoreAdamantine +ingotAdamantine +blockAdamantine +oreAtlarus +dustAtlarus +oreNetherAtlarus +denseoreAtlarus +ingotAtlarus +blockAtlarus +oreOsmium +oreNetherOsmium +denseoreOsmium +oreYellorite +dustYellorium +dustCyanite +oreNetherYellorite +denseoreYellorite +ingotYellorium +blockYellorium +denseoreArdite +denseoreCobalt +oreNetherQuartzBlack +denseoreQuartzBlack +oreUranium +oreNetherUranium +denseoreUranium +oreNetherSteel +oreNetherTitanium +denseoreTitanium +oreMagnesium +oreNetherMagnesium +denseoreMagnesium +oreTungsten +oreNetherTungsten +denseoreTungsten +oreNetherRutile +denseoreRutile +ingotRutile +oreNetherSalt +oreNetherDraconium +denseoreDraconium +oreProsperity +shardProsperity +oreNetherProsperity +oreEndProsperity +oreInferium +essenceInferium +oreNetherInferium +oreEndInferium +oreDimensionalShard +gemLava +gemEnderBiotite +oreBoron +oreCinnibar +dustCinnibar +crystalCinnibar +oreSheldonite +oreAstralStarmetal +dustAstralStarmetal +oreTritanium +oreEnder +dustRedstoneAlloy +ingotHepatizon +dustHepatizon +blockHepatizon +ingotAngmallen +dustAngmallen +blockAngmallen +ingotShadowSteel +dustShadowSteel +blockShadowSteel +ingotInolashite +dustInolashite +blockInolashite +ingotAmordrine +dustAmordrine +blockAmordrine +ingotQuicksilver +dustQuicksilver +blockQuicksilver +ingotHaderoth +dustHaderoth +blockHaderoth +ingotCelenegil +dustCelenegil +blockCelenegil +ingotTartarite +dustTartarite +blockTartarite +ingotCyanite +blockCyanite +ingotBlutonium +dustBlutonium +blockBlutonium +ingotLudicrite +dustLudicrite +blockLudicrite +ingotAluminumBrass +dustAluminumBrass +blockAluminumBrass +pulpWood +stairsWood +oreGarnet +gemGarnet +oreHeliodor +gemHeliodor +oreBeyrl +gemBeyrl +oreIndicolite +gemIndicolite +oreAquamarine +gemAquamarine +oreIolite +gemIolite +oreAgate +gemAgate +oreMorganite +gemMorganite +oreOnyx +gemOnyx +oreCarnelian +gemCarnelian +oreSpinel +gemSpinel +oreCitrine +gemCitrine +oreJasper +gemJasper +oreGoldenBeryl +gemGoldenBeryl +oreMoldavite +gemMoldavite +oreTurquoise +gemTurquoise +oreMoonstone +gemMoonstone +oreVioletSapphire +gemVioletSapphire +gemLepidolite +oreAmetrine +gemAmetrine +oreBlackDiamond +gemBlackDiamond +oreAlexandrite +gemAlexandrite +oreCoral +gemCoral +oreSunstone +gemSunstone +oreCatsEye +gemCatsEye +oreZircon +gemZircon +oreJade +gemJade +oreChrysoprase +gemChrysoprase +gemKyanite +oreAmmolite +gemAmmolite +oreKunzite +gemKunzite +oreRoseQuartz +gemRoseQuartz +oreTektite +gemTektite +orePearl +oreChaos +gemChaos +oreEnderEssence +gemEnderEssence +dustVoid +orePoorIron +orePoorGold +orePoorCopper +orePoorTin +orePoorLead +orePoorSilver +orePoorNickel +orePoorZinc + From 367cfe545140f3dec5ea1a7a1c51a8ef64751377 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Thu, 13 Nov 2025 03:39:56 +0100 Subject: [PATCH 09/18] add a temp rawchild field to regex nodes so it shows up when stringified --- src/types/parsing.ts | 1 + src/utils/parsers/OredicMatcher.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/types/parsing.ts b/src/types/parsing.ts index 126be15..7e26696 100644 --- a/src/types/parsing.ts +++ b/src/types/parsing.ts @@ -13,6 +13,7 @@ export type PatternNode = { export type RegexNode = { type: "regex"; negation: boolean; + rawChild: string; child: RegExp; }; diff --git a/src/utils/parsers/OredicMatcher.ts b/src/utils/parsers/OredicMatcher.ts index 7fb15e2..881b01b 100644 --- a/src/utils/parsers/OredicMatcher.ts +++ b/src/utils/parsers/OredicMatcher.ts @@ -52,6 +52,7 @@ export class OredicMatcher { return { type: "regex", negation: patternNode.negation, + rawChild: pattern, child: new RegExp(pattern), }; } From 74c8fe59c9ebf891f2b3f1c9628c8f05d67bab5e Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Thu, 13 Nov 2025 15:30:02 +0100 Subject: [PATCH 10/18] add matching --- src/types/parsing.ts | 8 ++++- src/utils/parsers/OredicMatcher.ts | 51 ++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/types/parsing.ts b/src/types/parsing.ts index 7e26696..e80a912 100644 --- a/src/types/parsing.ts +++ b/src/types/parsing.ts @@ -2,7 +2,7 @@ export type Ast = AstNode | null; export type exAst = exAstNode | null; export type AstNode = PatternNode | OperatorNode; -export type exAstNode = RegexNode | exOperatorNode | AstNode; +export type exAstNode = RegexNode | OredicNode | exOperatorNode | AstNode; export type PatternNode = { type: "pattern"; @@ -17,6 +17,12 @@ export type RegexNode = { child: RegExp; }; +export type OredicNode = { + type: "oredic"; + negation: boolean; + children: string[] | null; +}; + export type OperatorNode = AndNode | OrNode | XorNode; export type AndNode = { diff --git a/src/utils/parsers/OredicMatcher.ts b/src/utils/parsers/OredicMatcher.ts index 881b01b..80f81cb 100644 --- a/src/utils/parsers/OredicMatcher.ts +++ b/src/utils/parsers/OredicMatcher.ts @@ -1,5 +1,7 @@ +import { DUMPS } from "../../loaders/storage"; import { AstNode, + OredicNode, PatternNode, RegexNode, SupportedPack, @@ -15,18 +17,21 @@ export class OredicMatcher { private rules: AstNode, private pack: SupportedPack ) { + this.pack = "nomi-ceu"; this.ast = rules; this.validOredics = this.parse(this.ast); } parse(ast: exAst): string[] { this.parsePatterns(ast); - // 2nd parse: match logic to oredics. Return array of valid oredics + this.parseRegexes(ast); + // 2nd parse: match patterns to list of oredics + // 3nd parse: join/disjoin children based on the operators and negation return []; } private parsePatterns(ast: exAst): void { - if (!ast || ast.type === "regex") return; + if (!ast || ast.type === "regex" || ast.type === "oredic") return; if (ast.type === "pattern") { const regexNode = this.createRegex(ast); @@ -40,9 +45,29 @@ export class OredicMatcher { } } - private createRegex(patternNode: PatternNode): RegexNode { + private parseRegexes(ast: exAst): void { + if (!ast || ast.type === "pattern" || ast.type === "oredic") return; + + if (ast.type === "regex") { + const oredicNode = this.matchOredics(ast); + + if (!oredicNode) return; // Error + + delete (ast as any).children; + delete (ast as any).child; + delete (ast as any).rawChild; + Object.assign(ast, oredicNode); + return; + } + + for (const child of ast.children) { + this.parseRegexes(child); + } + } + + private createRegex(node: PatternNode): RegexNode { let pattern = ""; - for (const child of patternNode.children) { + for (const child of node.children) { if (child.type === "wildcard") { pattern += "[a-zA-Z]+"; } else { @@ -51,9 +76,23 @@ export class OredicMatcher { } return { type: "regex", - negation: patternNode.negation, + negation: node.negation, rawChild: pattern, - child: new RegExp(pattern), + child: new RegExp(pattern, "g"), + }; + } + + private matchOredics(node: RegexNode): OredicNode | null { + const dump = DUMPS.get(this.pack); + if (!dump) { + return null; + // Error + } + const oredicList = dump.match(node.child); + return { + type: "oredic", + negation: node.negation, + children: oredicList, }; } } From de570d3347f0e7aaf9899f045484061e86310429 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Thu, 13 Nov 2025 16:41:18 +0100 Subject: [PATCH 11/18] impl conjuction for the matcher --- src/utils/parsers/OredicMatcher.ts | 152 +++++++++++++++++++++++++++-- 1 file changed, 143 insertions(+), 9 deletions(-) diff --git a/src/utils/parsers/OredicMatcher.ts b/src/utils/parsers/OredicMatcher.ts index 80f81cb..a1ea57d 100644 --- a/src/utils/parsers/OredicMatcher.ts +++ b/src/utils/parsers/OredicMatcher.ts @@ -1,17 +1,23 @@ import { DUMPS } from "../../loaders/storage"; import { + AndNode, AstNode, - OredicNode, + OperatorNode, PatternNode, - RegexNode, SupportedPack, + exAndNode, exAst, + exOperatorNode, + RegexNode, + OredicNode, } from "../../types/parsing"; +import { MiscError } from "../../types/server"; import { getLogger } from "../Logger"; export class OredicMatcher { - validOredics: string[]; + validOredics: string[] | null; ast: exAst; + error: MiscError; constructor( private rules: AstNode, @@ -19,15 +25,26 @@ export class OredicMatcher { ) { this.pack = "nomi-ceu"; this.ast = rules; + + this.error = { + type: "error", + code: null, + status: false, + send: false, + message: null, + location: __dirname, + time: null, + }; + this.validOredics = this.parse(this.ast); } - parse(ast: exAst): string[] { + parse(ast: exAst): string[] | null { this.parsePatterns(ast); this.parseRegexes(ast); - // 2nd parse: match patterns to list of oredics - // 3nd parse: join/disjoin children based on the operators and negation - return []; + const finalNode = this.parseOredics(ast); + if (!finalNode) return null; + return finalNode.children; } private parsePatterns(ast: exAst): void { @@ -65,8 +82,50 @@ export class OredicMatcher { } } + private parseOredics(ast: exAst): OredicNode | null { + if (!ast) return null; + + if (ast.type === "oredic") return ast; + + if (ast.type !== "operator") { + this.setError( + 500, + "Unknown Error: Got something else than Oredic and Operator Nodes in the final parse" + ); + return null; + } + + for (let i = 0; i < ast.children.length; i++) { + const result = this.parseOredics(ast.children[i]); + if (result) { + ast.children[i] = result; + } + } + + let newNode: OredicNode | null = null; + switch (ast.operator) { + case "AND": + newNode = this.applyConjunction(ast); + break; + case "OR": + // TODO: implement OR logic + break; + case "XOR": + // TODO: implement XOR logic + break; + } + + if (newNode) { + delete (ast as any).operator; + Object.assign(ast, newNode); + return ast as unknown as OredicNode; + } + + return null; + } + private createRegex(node: PatternNode): RegexNode { - let pattern = ""; + let pattern = "^"; for (const child of node.children) { if (child.type === "wildcard") { pattern += "[a-zA-Z]+"; @@ -74,11 +133,12 @@ export class OredicMatcher { pattern += child.content; } } + pattern += "$"; return { type: "regex", negation: node.negation, rawChild: pattern, - child: new RegExp(pattern, "g"), + child: new RegExp(pattern, "gm"), }; } @@ -95,4 +155,78 @@ export class OredicMatcher { children: oredicList, }; } + + private applyConjunction(node: exAndNode): OredicNode { + const occurences = new Map(); + let newChildren: string[] = []; + + getLogger().formattingLog("Apply Conjunction"); + getLogger().simpleLog( + "debug", + `Number of children: ${node.children.length}` + ); + + for (const child of node.children) { + if (child.type !== "oredic") { + this.setError( + 500, + "Unknown Error: Tried to apply conjunction on something else than an oredic node" + ); + return { + type: "oredic", + negation: node.negation, + children: [], + }; + } + + if (!child.children || child.children.length === 0) { + getLogger().simpleLog( + "debug", + "Found empty child - returning empty array" + ); + return { + type: "oredic", + negation: node.negation, + children: [], + }; + } + + getLogger().simpleLog( + "debug", + `Child has ${child.children.length} oredics` + ); + + for (const oredic of child.children) { + const currentCount = occurences.get(oredic) ?? 0; + occurences.set(oredic, currentCount + 1); + } + } + + getLogger().simpleLog("debug", `Total unique oredics: ${occurences.size}`); + + occurences.forEach((value, key) => { + if (value === node.children.length) { + newChildren.push(key); + } + }); + + getLogger().simpleLog( + "debug", + `Common oredics found: ${newChildren.length}` + ); + + return { + type: "oredic", + negation: node.negation, + children: newChildren, + }; + } + + private setError(code: number, message: string): void { + this.error.code = code; + this.error.status = true; + this.error.send = true; + this.error.message = message; + this.error.time = new Date(); + } } From 57c076527d3bb1fa865d59011644787e987e6835 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Thu, 13 Nov 2025 17:05:07 +0100 Subject: [PATCH 12/18] apply remaining operators --- src/controllers/OredicController.ts | 2 +- src/utils/parsers/OredicMatcher.ts | 93 ++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 24 deletions(-) diff --git a/src/controllers/OredicController.ts b/src/controllers/OredicController.ts index dc2ced2..6268db0 100644 --- a/src/controllers/OredicController.ts +++ b/src/controllers/OredicController.ts @@ -19,7 +19,7 @@ export class OredicController { } const Matcher = new OredicMatcher(rules, "nomi-ceu"); - return Matcher.ast; + return Matcher.validOredics; } handler = diff --git a/src/utils/parsers/OredicMatcher.ts b/src/utils/parsers/OredicMatcher.ts index a1ea57d..5710b3a 100644 --- a/src/utils/parsers/OredicMatcher.ts +++ b/src/utils/parsers/OredicMatcher.ts @@ -10,6 +10,8 @@ import { exOperatorNode, RegexNode, OredicNode, + exOrNode, + exXorNode, } from "../../types/parsing"; import { MiscError } from "../../types/server"; import { getLogger } from "../Logger"; @@ -108,10 +110,10 @@ export class OredicMatcher { newNode = this.applyConjunction(ast); break; case "OR": - // TODO: implement OR logic + newNode = this.applyDisjunction(ast); break; case "XOR": - // TODO: implement XOR logic + newNode = this.applyExclusiveDisjunction(ast); break; } @@ -160,12 +162,6 @@ export class OredicMatcher { const occurences = new Map(); let newChildren: string[] = []; - getLogger().formattingLog("Apply Conjunction"); - getLogger().simpleLog( - "debug", - `Number of children: ${node.children.length}` - ); - for (const child of node.children) { if (child.type !== "oredic") { this.setError( @@ -180,10 +176,6 @@ export class OredicMatcher { } if (!child.children || child.children.length === 0) { - getLogger().simpleLog( - "debug", - "Found empty child - returning empty array" - ); return { type: "oredic", negation: node.negation, @@ -191,29 +183,84 @@ export class OredicMatcher { }; } - getLogger().simpleLog( - "debug", - `Child has ${child.children.length} oredics` - ); - for (const oredic of child.children) { const currentCount = occurences.get(oredic) ?? 0; occurences.set(oredic, currentCount + 1); } } - getLogger().simpleLog("debug", `Total unique oredics: ${occurences.size}`); - occurences.forEach((value, key) => { if (value === node.children.length) { newChildren.push(key); } }); - getLogger().simpleLog( - "debug", - `Common oredics found: ${newChildren.length}` - ); + return { + type: "oredic", + negation: node.negation, + children: newChildren, + }; + } + + private applyDisjunction(node: exOrNode): OredicNode { + let newChildren: string[] = []; + + for (const child of node.children) { + if (child.type !== "oredic") { + this.setError( + 500, + "Unknown Error: Tried to apply disjunction on something else than an oredic node" + ); + return { + type: "oredic", + negation: node.negation, + children: [], + }; + } + + const toConcat = child.children ? child.children : []; + newChildren = newChildren.concat(toConcat); + } + + return { + type: "oredic", + negation: node.negation, + children: newChildren, + }; + } + + private applyExclusiveDisjunction(node: exXorNode): OredicNode { + const occurences = new Map(); + let newChildren: string[] = []; + + for (const child of node.children) { + if (child.type !== "oredic") { + this.setError( + 500, + "Unknown Error: Tried to apply xor on something else than an oredic node" + ); + return { + type: "oredic", + negation: node.negation, + children: [], + }; + } + + if (!child.children || child.children.length === 0) { + continue; + } + + for (const oredic of child.children) { + const currentCount = occurences.get(oredic) ?? 0; + occurences.set(oredic, currentCount + 1); + } + } + + occurences.forEach((value, key) => { + if (value === 1) { + newChildren.push(key); + } + }); return { type: "oredic", From 6aca510a831bfd97f1eea4cac41d4006f054b001 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Thu, 13 Nov 2025 17:26:34 +0100 Subject: [PATCH 13/18] implement negation --- src/utils/parsers/OredicMatcher.ts | 90 +++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/src/utils/parsers/OredicMatcher.ts b/src/utils/parsers/OredicMatcher.ts index 5710b3a..fdace26 100644 --- a/src/utils/parsers/OredicMatcher.ts +++ b/src/utils/parsers/OredicMatcher.ts @@ -97,6 +97,14 @@ export class OredicMatcher { return null; } + if (ast.negation && (ast.operator === "AND" || ast.operator === "OR")) { + ast.operator = ast.operator === "AND" ? "OR" : "AND"; + ast.negation = false; + for (const child of ast.children) { + child.negation = !child.negation; + } + } + for (let i = 0; i < ast.children.length; i++) { const result = this.parseOredics(ast.children[i]); if (result) { @@ -160,7 +168,9 @@ export class OredicMatcher { private applyConjunction(node: exAndNode): OredicNode { const occurences = new Map(); + const toRemove = new Set(); let newChildren: string[] = []; + let nonNegatedCount = 0; for (const child of node.children) { if (child.type !== "oredic") { @@ -170,34 +180,56 @@ export class OredicMatcher { ); return { type: "oredic", - negation: node.negation, + negation: false, children: [], }; } if (!child.children || child.children.length === 0) { - return { - type: "oredic", - negation: node.negation, - children: [], - }; + if (!child.negation) { + return { + type: "oredic", + negation: false, + children: [], + }; + } + continue; } - for (const oredic of child.children) { - const currentCount = occurences.get(oredic) ?? 0; - occurences.set(oredic, currentCount + 1); + if (child.negation) { + for (const oredic of child.children) { + toRemove.add(oredic); + } + } else { + nonNegatedCount++; + for (const oredic of child.children) { + const currentCount = occurences.get(oredic) ?? 0; + occurences.set(oredic, currentCount + 1); + } } } + if (nonNegatedCount === 0) { + this.setError( + 500, + "AND operation requires at least one non-negated child" + ); + return { + type: "oredic", + negation: false, + children: [], + }; + } + occurences.forEach((value, key) => { - if (value === node.children.length) { + if (value === nonNegatedCount && !toRemove.has(key)) { newChildren.push(key); } }); return { type: "oredic", - negation: node.negation, + negation: false, children: newChildren, }; } @@ -213,18 +245,20 @@ export class OredicMatcher { ); return { type: "oredic", - negation: node.negation, + negation: false, children: [], }; } - const toConcat = child.children ? child.children : []; - newChildren = newChildren.concat(toConcat); + if (!child.negation) { + const toConcat = child.children ? child.children : []; + newChildren = newChildren.concat(toConcat); + } } return { type: "oredic", - negation: node.negation, + negation: false, children: newChildren, }; } @@ -241,7 +275,7 @@ export class OredicMatcher { ); return { type: "oredic", - negation: node.negation, + negation: false, children: [], }; } @@ -250,21 +284,39 @@ export class OredicMatcher { continue; } + const multiplier = child.negation ? -1 : 1; + for (const oredic of child.children) { const currentCount = occurences.get(oredic) ?? 0; - occurences.set(oredic, currentCount + 1); + occurences.set(oredic, currentCount + multiplier); } } occurences.forEach((value, key) => { - if (value === 1) { + if (Math.abs(value) % 2 === 1) { newChildren.push(key); } }); + if (node.negation) { + const dump = DUMPS.get(this.pack); + if (!dump) { + this.setError(500, "Failed to get dump for negated XOR operation"); + return { + type: "oredic", + negation: false, + children: [], + }; + } + + const allOredics = dump.split("\n").filter((line) => line.trim() !== ""); + const resultSet = new Set(newChildren); + newChildren = allOredics.filter((oredic) => !resultSet.has(oredic)); + } + return { type: "oredic", - negation: node.negation, + negation: false, children: newChildren, }; } From 1e0c3e094a8371d3746b950766209f1638d98034 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Fri, 14 Nov 2025 14:43:40 +0100 Subject: [PATCH 14/18] add a parent class "ErrorProne" that implements methods to return errors. --- src/types/errors.ts | 11 ++++++ src/types/server.ts | 10 ------ src/utils/parentClasses/ErrorProne.ts | 33 +++++++++++++++++ src/utils/parsers/OredicAe2.ts | 51 +++------------------------ src/utils/parsers/OredicMatcher.ts | 25 ++----------- src/utils/parsers/OredicSubstring.ts | 14 ++++++-- 6 files changed, 64 insertions(+), 80 deletions(-) create mode 100644 src/types/errors.ts create mode 100644 src/utils/parentClasses/ErrorProne.ts diff --git a/src/types/errors.ts b/src/types/errors.ts new file mode 100644 index 0000000..64bf4e2 --- /dev/null +++ b/src/types/errors.ts @@ -0,0 +1,11 @@ +import { PathLike } from "fs"; + +export type StandardError = { + type: "error"; + code: number | null; + status: boolean; + send: boolean; + message: string | null; + location: PathLike | null; + time: Date | null; +}; diff --git a/src/types/server.ts b/src/types/server.ts index ec2fadc..5787636 100644 --- a/src/types/server.ts +++ b/src/types/server.ts @@ -37,13 +37,3 @@ export type RequestEndpointConfig = { }; export type EndpointLevel = "source" | "main" | "child"; - -export type MiscError = { - type: "error"; - code: number | null; - status: boolean; - send: boolean; - message: string | null; - location: PathLike | null; - time: Date | null; -}; diff --git a/src/utils/parentClasses/ErrorProne.ts b/src/utils/parentClasses/ErrorProne.ts new file mode 100644 index 0000000..e685612 --- /dev/null +++ b/src/utils/parentClasses/ErrorProne.ts @@ -0,0 +1,33 @@ +import { StandardError } from "../../types/errors"; + +export class ErrorProne { + error: StandardError; + + constructor() { + this.error = { + type: "error", + code: null, + status: false, + send: false, + message: null, + location: __dirname, + time: null, + }; + } + + protected setError(code: number, message: string): void { + this.error.code = code; + this.error.status = true; + this.error.send = true; + this.error.message = message; + this.error.time = new Date(); + } + + protected setWarn(message: string): void { + this.error.code = 200; + this.error.status = true; + this.error.send = true; + this.error.message = message; + this.error.time = new Date(); + } +} diff --git a/src/utils/parsers/OredicAe2.ts b/src/utils/parsers/OredicAe2.ts index e74c989..fa50414 100644 --- a/src/utils/parsers/OredicAe2.ts +++ b/src/utils/parsers/OredicAe2.ts @@ -3,6 +3,7 @@ * https://github.com/AE2-UEL/Applied-Energistics-2/blob/26bb5986c636e9bdde62559b0d1c2bbc48c4b9e3/src/main/java/appeng/util/item/OreDictFilterMatcher.java#L11 */ +import { StandardError } from "../../types/errors"; import { Ast, AstNode, @@ -10,9 +11,7 @@ import { OperatorNode, Token, } from "../../types/parsing"; -import { MiscError } from "../../types/server"; -import { getLogger } from "../Logger"; -import { startTimer, stopTimer, Timer } from "../Timer"; +import { ErrorProne } from "../parentClasses/ErrorProne"; type LexemeElement = { type: "group" | "operator" | "negation" | "wildcard" | "text"; @@ -27,30 +26,19 @@ type ParseState = { negationFlag: boolean; }; -export class Ae2uelOredicParser { +export class Ae2uelOredicParser extends ErrorProne { lexemeBuffer: string; parenthesesCount: number; - error: MiscError; - private optimizationPipeline: Array<(node: AstNode) => void>; constructor(private oredicString: string) { + super(); this.lexemeBuffer = ""; this.parenthesesCount = 0; - this.error = { - type: "error", - code: null, - status: false, - send: false, - message: null, - location: __dirname, - time: null, - }; - this.optimizationPipeline = [ this.applyAssociativity.bind(this), this.applyAnnihilatorXor.bind(this), @@ -59,8 +47,7 @@ export class Ae2uelOredicParser { ]; } - parse(): Ast | MiscError { - startTimer("parser"); + parse(): Ast | StandardError { const { lexemeList } = this.lexicalParse(this.oredicString.split("")); if (this.parenthesesCount !== 0) { @@ -69,7 +56,6 @@ export class Ae2uelOredicParser { } const ast = this.parseNode(lexemeList); - const badAst = JSON.stringify(ast); if (this.error.status) { return this.error; @@ -81,17 +67,6 @@ export class Ae2uelOredicParser { return this.error; } - getLogger().formattingLog("Results:"); - getLogger().simpleLog("debug", `Input: ${this.oredicString}`); - - getLogger().simpleLog( - "debug", - `Time taken: ${stopTimer("parser").getTime().formatted}` - ); - getLogger().simpleLog( - "debug", - `Changed?: ${JSON.stringify(ast) !== badAst}` - ); return ast; } @@ -551,20 +526,4 @@ export class Ae2uelOredicParser { if (acceptedOperators === "all") return true; return acceptedOperators.includes(node.operator); } - - private setError(code: number, message: string): void { - this.error.code = code; - this.error.status = true; - this.error.send = true; - this.error.message = message; - this.error.time = new Date(); - } - - private setWarn(message: string): void { - this.error.code = 200; - this.error.status = true; - this.error.send = true; - this.error.message = message; - this.error.time = new Date(); - } } diff --git a/src/utils/parsers/OredicMatcher.ts b/src/utils/parsers/OredicMatcher.ts index fdace26..f686afd 100644 --- a/src/utils/parsers/OredicMatcher.ts +++ b/src/utils/parsers/OredicMatcher.ts @@ -13,31 +13,20 @@ import { exOrNode, exXorNode, } from "../../types/parsing"; -import { MiscError } from "../../types/server"; -import { getLogger } from "../Logger"; +import { ErrorProne } from "../parentClasses/ErrorProne"; -export class OredicMatcher { +export class OredicMatcher extends ErrorProne { validOredics: string[] | null; ast: exAst; - error: MiscError; constructor( private rules: AstNode, private pack: SupportedPack ) { + super(); this.pack = "nomi-ceu"; this.ast = rules; - this.error = { - type: "error", - code: null, - status: false, - send: false, - message: null, - location: __dirname, - time: null, - }; - this.validOredics = this.parse(this.ast); } @@ -320,12 +309,4 @@ export class OredicMatcher { children: newChildren, }; } - - private setError(code: number, message: string): void { - this.error.code = code; - this.error.status = true; - this.error.send = true; - this.error.message = message; - this.error.time = new Date(); - } } diff --git a/src/utils/parsers/OredicSubstring.ts b/src/utils/parsers/OredicSubstring.ts index 1fded55..780d1fe 100644 --- a/src/utils/parsers/OredicSubstring.ts +++ b/src/utils/parsers/OredicSubstring.ts @@ -1,5 +1,15 @@ +import { DUMPS } from "../../loaders/storage"; import { SupportedPack } from "../../types/parsing"; +import { ErrorProne } from "../parentClasses/ErrorProne"; -export class OredicSubstrings { - constructor(private pack: SupportedPack) {} +export class OredicSubstrings extends ErrorProne { + baseStrings: string[] | undefined; + + constructor(private pack: SupportedPack) { + super(); + this.baseStrings = DUMPS.get(pack)?.split("\n"); + if (!this.baseStrings) { + this.setError(500, "Failed to load oredic dump"); + } + } } From 4445a5dbad6f1c0f3f7d3f2fd0de623f2e75e5d5 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Sat, 15 Nov 2025 00:46:36 +0100 Subject: [PATCH 15/18] add a new class to find how to shorten strings. Also refactor a lot of the code --- .gitignore | 2 + src/controllers/OredicController.ts | 9 +- src/types/parsing.ts | 96 +++++-- src/utils/Logger.ts | 206 +++++++++++--- src/utils/Timer.ts | 9 + src/utils/parsers/OredicMatcher.ts | 116 ++++---- .../parsers/{OredicAe2.ts => OredicParser.ts} | 165 +++++++----- src/utils/parsers/OredicShortener.ts | 254 ++++++++++++++++++ src/utils/parsers/OredicSubstring.ts | 15 -- 9 files changed, 667 insertions(+), 205 deletions(-) rename src/utils/parsers/{OredicAe2.ts => OredicParser.ts} (80%) create mode 100644 src/utils/parsers/OredicShortener.ts delete mode 100644 src/utils/parsers/OredicSubstring.ts diff --git a/.gitignore b/.gitignore index b2c8fcd..7d23d01 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ logs/ ecosystem.config.js dist/ + +src/local/ diff --git a/src/controllers/OredicController.ts b/src/controllers/OredicController.ts index 6268db0..4150ac7 100644 --- a/src/controllers/OredicController.ts +++ b/src/controllers/OredicController.ts @@ -1,14 +1,13 @@ import { RequestHandler, Request, Response, NextFunction } from "express"; -import { Ae2uelOredicParser } from "../utils/parsers/OredicAe2"; -import { getLogger } from "../utils/Logger"; +import { OredicParser } from "../utils/parsers/OredicParser"; import { OredicMatcher } from "../utils/parsers/OredicMatcher"; export class OredicController { constructor() {} async answer(req: Request) { - const Parser = new Ae2uelOredicParser(req.body.string); - const rules = Parser.parse(); + const Parser = new OredicParser(); + const rules = Parser.parse(req.body.string); if (!rules) { // Catch unknown error return; @@ -19,7 +18,7 @@ export class OredicController { } const Matcher = new OredicMatcher(rules, "nomi-ceu"); - return Matcher.validOredics; + return Matcher.match(); } handler = diff --git a/src/types/parsing.ts b/src/types/parsing.ts index e80a912..66f0685 100644 --- a/src/types/parsing.ts +++ b/src/types/parsing.ts @@ -1,44 +1,69 @@ +export enum OperatorChar { + AND = "&", + OR = "|", + XOR = "^", +} + +export enum SpecialChar { + WILDCARD = "*", + NEGATION = "!", + GROUP_START = "(", + GROUP_END = ")", + SPACE = " ", +} + +export enum LexemeNames { + OPERATOR = "operator", + WILDCARD = "wildcard", + NEGATION = "negation", + GROUP = "group", + TEXT = "text", +} + +export enum NodeNames { + OPERATOR = "operator", + PATTERN = "pattern", + REGEX = "regex", + OREDIC = "oredic", + TEXT = "text", + WILDCARD = "wildcard", +} + +export type ParseState = { + ast: Ast; + tokenBuffer: Token[]; + operandBuffer: AstNode[]; + currentOperator: "AND" | "XOR" | "OR" | null; + negationFlag: boolean; +}; + export type Ast = AstNode | null; export type exAst = exAstNode | null; export type AstNode = PatternNode | OperatorNode; export type exAstNode = RegexNode | OredicNode | exOperatorNode | AstNode; -export type PatternNode = { - type: "pattern"; - negation: boolean; - children: Array; -}; - -export type RegexNode = { - type: "regex"; - negation: boolean; - rawChild: string; - child: RegExp; -}; +export type OperatorNode = AndNode | OrNode | XorNode; -export type OredicNode = { - type: "oredic"; - negation: boolean; - children: string[] | null; +export type LexemeElement = { + type: LexemeNames; + content: LexemeElement[] | string | null; }; -export type OperatorNode = AndNode | OrNode | XorNode; - export type AndNode = { - type: "operator"; + type: NodeNames.OPERATOR; operator: "AND"; negation: boolean; children: Array; }; export type OrNode = { - type: "operator"; + type: NodeNames.OPERATOR; operator: "OR"; negation: boolean; children: Array; }; export type XorNode = { - type: "operator"; + type: NodeNames.OPERATOR; operator: "XOR"; negation: boolean; children: Array; @@ -47,24 +72,45 @@ export type XorNode = { export type exOperatorNode = exAndNode | exOrNode | exXorNode; export type exAndNode = { - type: "operator"; + type: NodeNames.OPERATOR; operator: "AND"; negation: boolean; children: Array; }; export type exOrNode = { - type: "operator"; + type: NodeNames.OPERATOR; operator: "OR"; negation: boolean; children: Array; }; export type exXorNode = { - type: "operator"; + type: NodeNames.OPERATOR; operator: "XOR"; negation: boolean; children: Array; }; -export type Token = { type: "text"; content: string } | { type: "wildcard" }; +export type PatternNode = { + type: NodeNames.PATTERN; + negation: boolean; + children: Array; +}; + +export type RegexNode = { + type: NodeNames.REGEX; + negation: boolean; + rawChild: string; + child: RegExp; +}; + +export type OredicNode = { + type: NodeNames.OREDIC; + negation: boolean; + children: string[]; +}; + +export type Token = + | { type: NodeNames.TEXT; content: string } + | { type: NodeNames.WILDCARD }; export type SupportedPack = "nomi-ceu"; diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index 4218794..7367795 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -1,53 +1,189 @@ import { config } from "../config/config"; import { LogType } from "../types/server"; +import { queryTimer } from "./Timer"; let logger: Logger | null = null; +enum AnsiColor { + SUCCESS = "\x1b[32m", + INFO = "\x1b[36m", + WARN = "\x1b[33m", + ERROR = "\x1b[31m", + DEBUG = "\x1b[35m", + FORMAT = "\x1b[37m", + RESET = "\x1b[0m", +} + +enum FormattingConstant { + MAX_TYPE_LENGTH = 7, // "success".length + PROGRESS_BAR_FILLED = "█", + PROGRESS_BAR_EMPTY = "░", + FORMATTING_DASH = "-", + FORMATTING_TARGET_WIDTH = 50, + SECONDS_PER_MINUTE = 60, + MILLISECONDS_PER_SECOND = 1000, +} + export class Logger { - name: string; + private name: string; constructor() { this.name = config.LOGGER_NAME; } - private static colors = { - success: "\x1b[32m", // Green - info: "\x1b[36m", // Cyan - warn: "\x1b[33m", // Yellow - error: "\x1b[31m", // Red - debug: "\x1b[35m", // Magenta - format: "\x1b[37m", // White - reset: "\x1b[0m", - }; - - simpleLog(type: LogType, message: string) { - const name = `[${this.name}:${type.toUpperCase()}]`; - const date = `[${this.getDateTime()}]`; - const whitespaces = " ".repeat("success".length - type.length); - const content = message; - const color = Logger.colors[type]; - const log = `${color}${name}${whitespaces}@${date}: ${content}${Logger.colors.reset}`; - console.log(log); + simpleLog(type: LogType, message: string): void { + const timestamp = this.getCurrentTimestamp(); + const logPrefix = this.formatLogPrefix(type, timestamp); + const coloredMessage = this.colorize(message, type); + + console.log(`${logPrefix}: ${coloredMessage}`); + } + + formattingLog(title: string): void { + const timestamp = this.getCurrentTimestamp(); + const logPrefix = this.formatLogPrefix("format", timestamp); + const formattedTitle = this.formatTitle(title); + const coloredTitle = this.colorize(formattedTitle, "format"); + + console.log(`${logPrefix}: ${coloredTitle}`); + } + + progressBar( + currentCount: number, + totalCount: number, + timerId: string, + barWidth: number = 50, + label: string = "" + ): void { + const progress = this.buildProgressBar( + currentCount, + totalCount, + timerId, + barWidth, + label + ); + + const coloredProgress = this.colorize(progress, "info"); + process.stdout.write(`\r${coloredProgress}`); + + const isComplete = currentCount === totalCount; + if (isComplete) { + process.stdout.write("\n"); + } } - formattingLog(title: string) { - const type = "format"; - const name = `[${this.name}:${type.toUpperCase()}]`; - const date = `[${this.getDateTime()}]`; - const whitespaces = " ".repeat("success".length - type.length); - const targetLength = 50; - const dashesNb = (targetLength - title.length) / 2; - const dashes = "-".repeat(dashesNb); - const extraDash = dashesNb % 1 != 0 ? "-" : ""; - const content = `|${dashes} ${title} ${dashes}${extraDash}|`; - const color = Logger.colors[type]; - const log = `${color}${name}${whitespaces}@${date}: ${content}${Logger.colors.reset}`; - console.log(log); + /* + * Formatting helpers + */ + + private formatLogPrefix(type: LogType | "format", timestamp: string): string { + const logName = `[${this.name}:${type.toUpperCase()}]`; + const logTimestamp = `[${timestamp}]`; + const padding = " ".repeat( + FormattingConstant.MAX_TYPE_LENGTH - type.length + ); + + return `${logName}${padding}@${logTimestamp}`; + } + + private formatTitle(title: string): string { + const dashCount = + (FormattingConstant.FORMATTING_TARGET_WIDTH - title.length) / 2; + const dashes = (FormattingConstant.FORMATTING_DASH as string).repeat( + dashCount + ); + const hasOddLength = dashCount % 1 !== 0; + const extraDash = hasOddLength ? FormattingConstant.FORMATTING_DASH : ""; + + return `|${dashes} ${title} ${dashes}${extraDash}|`; + } + + private buildProgressBar( + currentCount: number, + totalCount: number, + timerId: string, + barWidth: number, + label: string + ): string { + const percentage = Math.floor((currentCount / totalCount) * 100); + const bar = this.createBar(currentCount, totalCount, barWidth); + const timeInfo = this.createTimeInfo(currentCount, totalCount, timerId); + const labelText = label ? ` ${label}` : ""; + + return `[${bar}] ${percentage}% (${currentCount}/${totalCount})${labelText} ${timeInfo}`; + } + + private createBar( + currentCount: number, + totalCount: number, + barWidth: number + ): string { + const filledWidth = Math.floor((currentCount / totalCount) * barWidth); + const emptyWidth = Math.max(0, barWidth - filledWidth); + + const filled = (FormattingConstant.PROGRESS_BAR_FILLED as string).repeat( + filledWidth + ); + const empty = (FormattingConstant.PROGRESS_BAR_EMPTY as string).repeat( + emptyWidth + ); + + return `${filled}${empty}`; + } + + private createTimeInfo( + currentCount: number, + totalCount: number, + timerId: string + ): string { + const elapsedMilliseconds = queryTimer(timerId).getTime("ms").raw; + const elapsedSeconds = Math.floor( + elapsedMilliseconds / FormattingConstant.MILLISECONDS_PER_SECOND + ); + const elapsedFormatted = this.formatTime(elapsedSeconds); + + let etaText = ""; + const isInProgress = currentCount < totalCount && currentCount > 0; + + if (isInProgress) { + const remainingMilliseconds = + (elapsedMilliseconds / currentCount) * (totalCount - currentCount); + const remainingSeconds = Math.floor( + remainingMilliseconds / FormattingConstant.MILLISECONDS_PER_SECOND + ); + const etaFormatted = this.formatTime(remainingSeconds); + etaText = ` ETA: ${etaFormatted}`; + } + + return `[${elapsedFormatted}]${etaText}`; + } + + private formatTime(totalSeconds: number): string { + const minutes = Math.floor( + totalSeconds / FormattingConstant.SECONDS_PER_MINUTE + ); + const seconds = totalSeconds % FormattingConstant.SECONDS_PER_MINUTE; + const paddedSeconds = seconds.toString().padStart(2, "0"); + + return `${minutes}:${paddedSeconds}`; + } + + private colorize(text: string, type: LogType | "format"): string { + const colorMap: Record = { + success: AnsiColor.SUCCESS, + info: AnsiColor.INFO, + warn: AnsiColor.WARN, + error: AnsiColor.ERROR, + debug: AnsiColor.DEBUG, + format: AnsiColor.FORMAT, + }; + + const color = colorMap[type]; + return `${color}${text}${AnsiColor.RESET}`; } - private getDateTime() { - const rawTimestamp = new Date(); - return rawTimestamp.toISOString(); + private getCurrentTimestamp(): string { + return new Date().toISOString(); } } diff --git a/src/utils/Timer.ts b/src/utils/Timer.ts index 5729cc6..157a198 100644 --- a/src/utils/Timer.ts +++ b/src/utils/Timer.ts @@ -81,6 +81,15 @@ export function stopTimer(id: string): Timer { return time; } +export function queryTimer(id: string): Timer { + const time = timers.get(id); + if (!time) + throw new Error( + `Timer ${id} not initialized. Call startTimer(id:string) first.` + ); + return time; +} + export function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } diff --git a/src/utils/parsers/OredicMatcher.ts b/src/utils/parsers/OredicMatcher.ts index f686afd..c1a3606 100644 --- a/src/utils/parsers/OredicMatcher.ts +++ b/src/utils/parsers/OredicMatcher.ts @@ -1,36 +1,68 @@ import { DUMPS } from "../../loaders/storage"; import { - AndNode, AstNode, - OperatorNode, - PatternNode, SupportedPack, - exAndNode, exAst, - exOperatorNode, RegexNode, OredicNode, + exAndNode, exOrNode, exXorNode, + PatternNode, + NodeNames, } from "../../types/parsing"; import { ErrorProne } from "../parentClasses/ErrorProne"; +import { OredicParser } from "./OredicParser"; + +const EMPTY_OREDIC_NODE: OredicNode = { + type: NodeNames.OREDIC, + negation: false, + children: [], +} as const; export class OredicMatcher extends ErrorProne { - validOredics: string[] | null; - ast: exAst; + private validOredics: string[] | null; + private ast: exAst; + private parser: OredicParser; constructor( - private rules: AstNode, + rules: AstNode | PatternNode | RegexNode | OredicNode | string, private pack: SupportedPack ) { super(); this.pack = "nomi-ceu"; - this.ast = rules; + this.parser = new OredicParser(); + this.ast = this.parseInputRules(rules); + this.validOredics = null; + } + + private parseInputRules( + rules: AstNode | RegexNode | OredicNode | string + ): exAst { + if (typeof rules === "string") { + const parseResult = this.parser.parse(rules); + const isErrorResult = + !parseResult || (parseResult as any).type === "error"; + return isErrorResult ? null : (parseResult as AstNode); + } + + return rules; + } + + match(): string[] | null { + if (this.validOredics !== null) { + return this.validOredics; + } this.validOredics = this.parse(this.ast); + return this.validOredics; } - parse(ast: exAst): string[] | null { + /* + * AST transformation pipeline + */ + + private parse(ast: exAst): string[] | null { this.parsePatterns(ast); this.parseRegexes(ast); const finalNode = this.parseOredics(ast); @@ -134,7 +166,7 @@ export class OredicMatcher extends ErrorProne { } pattern += "$"; return { - type: "regex", + type: NodeNames.REGEX, negation: node.negation, rawChild: pattern, child: new RegExp(pattern, "gm"), @@ -148,11 +180,7 @@ export class OredicMatcher extends ErrorProne { // Error } const oredicList = dump.match(node.child); - return { - type: "oredic", - negation: node.negation, - children: oredicList, - }; + return this.newOredicNode(oredicList); } private applyConjunction(node: exAndNode): OredicNode { @@ -167,20 +195,12 @@ export class OredicMatcher extends ErrorProne { 500, "Unknown Error: Tried to apply conjunction on something else than an oredic node" ); - return { - type: "oredic", - negation: false, - children: [], - }; + return this.newOredicNode(null); } if (!child.children || child.children.length === 0) { if (!child.negation) { - return { - type: "oredic", - negation: false, - children: [], - }; + return this.newOredicNode(null); } continue; } @@ -203,11 +223,7 @@ export class OredicMatcher extends ErrorProne { 500, "AND operation requires at least one non-negated child" ); - return { - type: "oredic", - negation: false, - children: [], - }; + return this.newOredicNode(null); } occurences.forEach((value, key) => { @@ -216,11 +232,7 @@ export class OredicMatcher extends ErrorProne { } }); - return { - type: "oredic", - negation: false, - children: newChildren, - }; + return this.newOredicNode(newChildren); } private applyDisjunction(node: exOrNode): OredicNode { @@ -232,11 +244,7 @@ export class OredicMatcher extends ErrorProne { 500, "Unknown Error: Tried to apply disjunction on something else than an oredic node" ); - return { - type: "oredic", - negation: false, - children: [], - }; + return this.newOredicNode(null); } if (!child.negation) { @@ -245,11 +253,7 @@ export class OredicMatcher extends ErrorProne { } } - return { - type: "oredic", - negation: false, - children: newChildren, - }; + return this.newOredicNode(newChildren); } private applyExclusiveDisjunction(node: exXorNode): OredicNode { @@ -262,11 +266,7 @@ export class OredicMatcher extends ErrorProne { 500, "Unknown Error: Tried to apply xor on something else than an oredic node" ); - return { - type: "oredic", - negation: false, - children: [], - }; + return this.newOredicNode(null); } if (!child.children || child.children.length === 0) { @@ -291,11 +291,7 @@ export class OredicMatcher extends ErrorProne { const dump = DUMPS.get(this.pack); if (!dump) { this.setError(500, "Failed to get dump for negated XOR operation"); - return { - type: "oredic", - negation: false, - children: [], - }; + return this.newOredicNode(null); } const allOredics = dump.split("\n").filter((line) => line.trim() !== ""); @@ -303,10 +299,14 @@ export class OredicMatcher extends ErrorProne { newChildren = allOredics.filter((oredic) => !resultSet.has(oredic)); } + return this.newOredicNode(newChildren); + } + + private newOredicNode(children: string[] | null): OredicNode { return { - type: "oredic", + type: NodeNames.OREDIC, negation: false, - children: newChildren, + children: children ? children : [], }; } } diff --git a/src/utils/parsers/OredicAe2.ts b/src/utils/parsers/OredicParser.ts similarity index 80% rename from src/utils/parsers/OredicAe2.ts rename to src/utils/parsers/OredicParser.ts index fa50414..9277c8f 100644 --- a/src/utils/parsers/OredicAe2.ts +++ b/src/utils/parsers/OredicParser.ts @@ -10,33 +10,23 @@ import { PatternNode, OperatorNode, Token, + OperatorChar, + SpecialChar, + NodeNames, + LexemeElement, + LexemeNames, + ParseState, } from "../../types/parsing"; import { ErrorProne } from "../parentClasses/ErrorProne"; -type LexemeElement = { - type: "group" | "operator" | "negation" | "wildcard" | "text"; - content: LexemeElement[] | string | null; -}; - -type ParseState = { - ast: Ast; - tokenBuffer: Token[]; - operandBuffer: AstNode[]; - currentOperator: "AND" | "XOR" | "OR" | null; - negationFlag: boolean; -}; - -export class Ae2uelOredicParser extends ErrorProne { - lexemeBuffer: string; - - parenthesesCount: number; - +export class OredicParser extends ErrorProne { + private lexemeBuffer: string; + private parenthesesCount: number; private optimizationPipeline: Array<(node: AstNode) => void>; - constructor(private oredicString: string) { + constructor() { super(); this.lexemeBuffer = ""; - this.parenthesesCount = 0; this.optimizationPipeline = [ @@ -47,8 +37,10 @@ export class Ae2uelOredicParser extends ErrorProne { ]; } - parse(): Ast | StandardError { - const { lexemeList } = this.lexicalParse(this.oredicString.split("")); + parse(input: string): Ast | StandardError { + this.resetState(); + + const { lexemeList } = this.lexicalParse(input.split("")); if (this.parenthesesCount !== 0) { this.setError(400, "Unbalanced parentheses"); @@ -71,7 +63,19 @@ export class Ae2uelOredicParser extends ErrorProne { } /* - * Main methods + * State management + */ + + private resetState(): void { + this.lexemeBuffer = ""; + this.parenthesesCount = 0; + this.error.status = false; + this.error.code = null; + this.error.message = null; + } + + /* + * Lexical parsing */ private lexicalParse(pattern: string[]): { @@ -82,45 +86,48 @@ export class Ae2uelOredicParser extends ErrorProne { for (let i = 0; i < pattern.length; i++) { switch (pattern[i]) { - case " ": + case SpecialChar.SPACE: break; - case "(": + case SpecialChar.GROUP_START: lexemeList = this.flushLexemeBuffer(lexemeList); - this.updateParCount("START"); + this.incrementParentheses(); const result = this.lexicalParse(pattern.slice(i + 1)); - lexemeList.push({ type: "group", content: result.lexemeList }); + lexemeList.push({ + type: LexemeNames.GROUP, + content: result.lexemeList, + }); i += result.consumed; break; - case ")": + case SpecialChar.GROUP_END: lexemeList = this.flushLexemeBuffer(lexemeList); - this.updateParCount("END"); + this.decrementParentheses(); return { lexemeList, consumed: i + 1 }; - case "&": + case OperatorChar.AND: lexemeList = this.flushLexemeBuffer(lexemeList); - lexemeList.push({ type: "operator", content: "AND" }); + lexemeList.push({ type: LexemeNames.OPERATOR, content: "AND" }); break; - case "^": + case OperatorChar.XOR: lexemeList = this.flushLexemeBuffer(lexemeList); - lexemeList.push({ type: "operator", content: "XOR" }); + lexemeList.push({ type: LexemeNames.OPERATOR, content: "XOR" }); break; - case "|": + case OperatorChar.OR: lexemeList = this.flushLexemeBuffer(lexemeList); - lexemeList.push({ type: "operator", content: "OR" }); + lexemeList.push({ type: LexemeNames.OPERATOR, content: "OR" }); break; - case "!": + case SpecialChar.NEGATION: lexemeList = this.flushLexemeBuffer(lexemeList); - lexemeList.push({ type: "negation", content: null }); + lexemeList.push({ type: LexemeNames.NEGATION, content: null }); break; - case "*": + case SpecialChar.WILDCARD: lexemeList = this.flushLexemeBuffer(lexemeList); - lexemeList.push({ type: "wildcard", content: null }); + lexemeList.push({ type: LexemeNames.WILDCARD, content: null }); break; default: @@ -203,7 +210,7 @@ export class Ae2uelOredicParser extends ErrorProne { break; case "wildcard": - state.tokenBuffer.push({ type: "wildcard" }); + state.tokenBuffer.push({ type: NodeNames.WILDCARD }); break; case "text": @@ -212,7 +219,7 @@ export class Ae2uelOredicParser extends ErrorProne { this.setError(400, "Invalid text content"); return null; } - state.tokenBuffer.push({ type: "text", content: content }); + state.tokenBuffer.push({ type: NodeNames.TEXT, content: content }); break; } } @@ -235,7 +242,7 @@ export class Ae2uelOredicParser extends ErrorProne { fn(currentAst); } - if (currentAst.type === "pattern") return; + if (currentAst.type === NodeNames.PATTERN) return; for (const child of currentAst.children) { this.flattenAst(child); @@ -246,16 +253,15 @@ export class Ae2uelOredicParser extends ErrorProne { } /* - * Helper methods + * Buffer management */ private flushLexemeBuffer(lexemeList: LexemeElement[]): LexemeElement[] { if (this.lexemeBuffer === "") { return lexemeList; } - lexemeList.push({ type: "text", content: this.lexemeBuffer }); + lexemeList.push({ type: LexemeNames.TEXT, content: this.lexemeBuffer }); this.lexemeBuffer = ""; - return lexemeList; } @@ -285,7 +291,7 @@ export class Ae2uelOredicParser extends ErrorProne { } state.ast = { - type: "operator", + type: NodeNames.OPERATOR, operator: operator, negation: false, children: state.operandBuffer, @@ -294,14 +300,17 @@ export class Ae2uelOredicParser extends ErrorProne { return; } - if (state.ast.type === "operator" && state.ast.operator === operator) { + if ( + state.ast.type === NodeNames.OPERATOR && + state.ast.operator === operator + ) { state.ast.children = state.ast.children.concat(state.operandBuffer); state.operandBuffer = []; return; } state.ast = { - type: "operator", + type: NodeNames.OPERATOR, operator: operator, negation: false, children: [state.ast].concat(state.operandBuffer), @@ -330,28 +339,49 @@ export class Ae2uelOredicParser extends ErrorProne { return state.ast; } - private updateParCount(side: "START" | "END"): void { - switch (side) { - case "START": - this.parenthesesCount += 1; - break; + /* + * Character classification and operator helpers + */ - case "END": - if (this.parenthesesCount === 0) { - this.setError(400, "Closing parenthesis without opening"); - return; - } - this.parenthesesCount -= 1; - break; + private isOperatorChar(char: string): boolean { + return ( + char === OperatorChar.AND || + char === OperatorChar.OR || + char === OperatorChar.XOR + ); + } + + private getOperatorName(char: string): "AND" | "OR" | "XOR" { + switch (char) { + case OperatorChar.AND: + return "AND"; + case OperatorChar.OR: + return "OR"; + case OperatorChar.XOR: + return "XOR"; + default: + throw new Error(`Invalid operator character: ${char}`); } } + private incrementParentheses(): void { + this.parenthesesCount += 1; + } + + private decrementParentheses(): void { + if (this.parenthesesCount === 0) { + this.setError(400, "Closing parenthesis without opening"); + return; + } + this.parenthesesCount -= 1; + } + private createPatternNode( children: Token[], negation: boolean = false ): PatternNode { const node: PatternNode = { - type: "pattern", + type: NodeNames.PATTERN, negation: negation, children: children, }; @@ -373,7 +403,7 @@ export class Ae2uelOredicParser extends ErrorProne { for (let i = 0; i < operatorNode.children.length; i++) { const child = operatorNode.children[i]; if ( - child.type === "operator" && + child.type === NodeNames.OPERATOR && child.operator === topLevelOperator && child.negation === false ) { @@ -504,25 +534,26 @@ export class Ae2uelOredicParser extends ErrorProne { node = operatorNode; } - // Conjunction for wildcards: Token(a, wildcard, wildcard, b) = Token(a, wildcard, b) private applyWildcardConjunction(node: AstNode): void { - if (node.type !== "pattern") return; + if (node.type !== NodeNames.PATTERN) return; for (let i = 1; i < node.children.length; i++) { const lastChild = node.children[i - 1]; const currChild = node.children[i]; - if (currChild.type === "wildcard" && lastChild.type === "wildcard") { + if ( + currChild.type === NodeNames.WILDCARD && + lastChild.type === NodeNames.WILDCARD + ) { node.children.splice(i, 1); i -= 1; } } } - // Helper to check if an operator is in the list of accepted operators private operatorAccepted( node: AstNode, acceptedOperators: Array<"AND" | "OR" | "XOR"> | "all" ): boolean { - if (node.type === "pattern") return false; + if (node.type === NodeNames.PATTERN) return false; if (acceptedOperators === "all") return true; return acceptedOperators.includes(node.operator); } diff --git a/src/utils/parsers/OredicShortener.ts b/src/utils/parsers/OredicShortener.ts new file mode 100644 index 0000000..bc5ee7c --- /dev/null +++ b/src/utils/parsers/OredicShortener.ts @@ -0,0 +1,254 @@ +import { DUMPS } from "../../loaders/storage"; +import { Ast, exAst, NodeNames, SupportedPack } from "../../types/parsing"; +import { getLogger } from "../Logger"; +import { ErrorProne } from "../parentClasses/ErrorProne"; +import { OredicMatcher } from "./OredicMatcher"; +import { startTimer } from "../Timer"; + +const PROGRESS_UPDATE_INTERVAL = 10; + +export class OredicSubstrings extends ErrorProne { + private dump: string[]; + private shortestPatterns: Map; + + constructor(private pack: SupportedPack) { + super(); + this.dump = this.loadDump(); + this.shortestPatterns = new Map(); + this.findShortestPatterns(); + } + + /* + * Main processing + */ + simplifyPatterns(ast: Ast) { + if (!ast) return null; + + if (ast.type === NodeNames.PATTERN) { + for (const child of ast.children) { + if (child.type === NodeNames.WILDCARD) continue; + + const newText = this.shortestPatterns.get(child.content); + + if (!newText) continue; + + delete (child as any).content; + child.content = newText; + } + return ast; + } + + for (const child of ast.children) { + this.simplifyPatterns(child); + } + } + + private findShortestPatterns(): void { + getLogger().simpleLog( + "info", + `Finding shortest unique patterns for ${this.pack}` + ); + startTimer("substrings"); + + for (let position = 0; position < this.dump.length; position++) { + const oredicName = this.dump[position]; + const shortestPattern = this.findShortestUniquePattern(oredicName); + this.shortestPatterns.set(oredicName, shortestPattern); + + const currentIndex = position + 1; + const shouldUpdateProgress = + currentIndex % PROGRESS_UPDATE_INTERVAL === 0 || + currentIndex === this.dump.length; + + if (shouldUpdateProgress) { + this.updateProgress(currentIndex, this.dump.length); + } + } + + getLogger().simpleLog( + "success", + `Found shortest patterns for ${this.dump.length} oredics` + ); + } + + private findShortestUniquePattern(targetOredic: string): string { + const patternGenerators = [ + () => this.generateSingleCharPatterns(targetOredic), + () => this.generateTwoCharPatterns(targetOredic), + () => this.generateThreeCharPatterns(targetOredic), + () => this.generatePrefixSuffixPatterns(targetOredic), + () => [targetOredic], + ]; + + for (const generator of patternGenerators) { + const candidatePatterns = generator(); + + for (const pattern of candidatePatterns) { + if (this.isUniqueMatch(pattern, targetOredic)) { + return pattern; + } + } + } + + return targetOredic; + } + + /* + * Pattern validation + */ + + private isUniqueMatch(pattern: string, targetOredic: string): boolean { + const matcher = new OredicMatcher(pattern, this.pack); + const matchingOredics = matcher.match(); + + const hasExactlyOneMatch = matchingOredics?.length === 1; + const matchesTarget = matchingOredics?.[0] === targetOredic; + + return hasExactlyOneMatch && matchesTarget; + } + + /* + * Pattern generation + */ + + private generateSingleCharPatterns(text: string): string[] { + const patterns: string[] = []; + const lastIndex = text.length - 1; + + for (let index = 0; index < text.length; index++) { + const char = text[index]; + const isFirstChar = index === 0; + const isLastChar = index === lastIndex; + + patterns.push(`*${char}*`); + + if (isFirstChar) { + patterns.push(`${char}*`); + } + + if (isLastChar) { + patterns.push(`*${char}`); + } + } + + return patterns; + } + + private generateTwoCharPatterns(text: string): string[] { + const patterns: string[] = []; + + this.generateConsecutivePairs(text, patterns); + this.generateNonConsecutivePairs(text, patterns); + + return patterns; + } + + private generateConsecutivePairs(text: string, patterns: string[]): void { + const lastPairIndex = text.length - 2; + + for (let index = 0; index < text.length - 1; index++) { + const pair = text.substring(index, index + 2); + const isFirstPair = index === 0; + const isLastPair = index === lastPairIndex; + + patterns.push(`*${pair}*`); + + if (isFirstPair) { + patterns.push(`${pair}*`); + } + + if (isLastPair) { + patterns.push(`*${pair}`); + } + } + } + + private generateNonConsecutivePairs(text: string, patterns: string[]): void { + const lastIndex = text.length - 1; + + for (let firstIndex = 0; firstIndex < text.length - 1; firstIndex++) { + const firstChar = text[firstIndex]; + + for ( + let secondIndex = firstIndex + 2; + secondIndex < text.length; + secondIndex++ + ) { + const secondChar = text[secondIndex]; + const isAtBoundary = firstIndex > 0 || secondIndex < lastIndex; + + patterns.push(`${firstChar}*${secondChar}`); + + if (isAtBoundary) { + patterns.push(`*${firstChar}*${secondChar}*`); + } + } + } + } + + private generateThreeCharPatterns(text: string): string[] { + const patterns: string[] = []; + const lastTripletIndex = text.length - 3; + + for (let index = 0; index < text.length - 2; index++) { + const triplet = text.substring(index, index + 3); + const isFirstTriplet = index === 0; + const isLastTriplet = index === lastTripletIndex; + + patterns.push(`*${triplet}*`); + + if (isFirstTriplet) { + patterns.push(`${triplet}*`); + } + + if (isLastTriplet) { + patterns.push(`*${triplet}`); + } + } + + return patterns; + } + + private generatePrefixSuffixPatterns(text: string): string[] { + const patterns: string[] = []; + + for (let length = 4; length < text.length; length++) { + const prefix = text.substring(0, length); + const suffixStartIndex = text.length - length; + const suffix = text.substring(suffixStartIndex); + + patterns.push(`${prefix}*`); + patterns.push(`*${suffix}`); + } + + return patterns; + } + + /* + * Utility methods + */ + + private loadDump(): string[] { + const dumpContent = DUMPS.get(this.pack); + + if (!dumpContent) { + throw new Error(`Failed to load oredic dump for pack: ${this.pack}`); + } + + return dumpContent.split("\n"); + } + + private updateProgress(currentCount: number, totalCount: number): void { + const PROGRESS_BAR_WIDTH = 50; + const TIMER_ID = "substrings"; + const PROGRESS_LABEL = "Processing oredics"; + + getLogger().progressBar( + currentCount, + totalCount, + TIMER_ID, + PROGRESS_BAR_WIDTH, + PROGRESS_LABEL + ); + } +} diff --git a/src/utils/parsers/OredicSubstring.ts b/src/utils/parsers/OredicSubstring.ts deleted file mode 100644 index 780d1fe..0000000 --- a/src/utils/parsers/OredicSubstring.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { DUMPS } from "../../loaders/storage"; -import { SupportedPack } from "../../types/parsing"; -import { ErrorProne } from "../parentClasses/ErrorProne"; - -export class OredicSubstrings extends ErrorProne { - baseStrings: string[] | undefined; - - constructor(private pack: SupportedPack) { - super(); - this.baseStrings = DUMPS.get(pack)?.split("\n"); - if (!this.baseStrings) { - this.setError(500, "Failed to load oredic dump"); - } - } -} From 6ab81ba6d3ae09f27464db65f680952471cf7806 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Mon, 17 Nov 2025 03:39:53 +0100 Subject: [PATCH 16/18] add a shortener, a builder, and refactor basically the entire codebase --- src/bot/EventHandler.ts | 2 +- src/config/config.ts | 2 +- src/config/routes.ts | 11 + src/controllers/AiController.ts | 27 ++ src/controllers/GrokController.ts | 73 ----- src/controllers/MembersController.ts | 111 ++----- src/controllers/OredicController.ts | 39 +-- src/middleware/analytics/perf/timer.ts | 23 ++ src/middleware/auth/globalRateLimits.ts | 49 +++ src/middleware/{ => auth}/leveretAuth.ts | 8 +- src/middleware/auth/userRateLimits.ts | 52 +++ src/middleware/cleanup.ts | 29 -- src/middleware/endpointData.ts | 74 ----- src/middleware/endpoints/aiEndpoint.ts | 44 +++ src/middleware/endpoints/loadEndpoint.ts | 12 + src/middleware/endpoints/membersEndpoint.ts | 10 + src/middleware/endpoints/oredicEndpoint.ts | 41 +++ src/middleware/globalRateLimits.ts | 45 --- src/middleware/sanity/aiTokenChecker.ts | 65 ++++ src/middleware/sanity/cleanup.ts | 26 ++ src/middleware/{ => sanity}/filterBody.ts | 22 +- .../{ => sanity}/jsonWithRawBody.ts | 0 src/middleware/{ => sanity}/validator.ts | 2 +- src/middleware/setEndpointData.ts | 36 -- src/middleware/tokenChecker.ts | 49 --- src/middleware/userRateLimits.ts | 48 --- src/routes/ai.ts | 37 +++ src/routes/data/data.ts | 11 - src/routes/data/members/members.ts | 34 -- src/routes/index.ts | 34 +- src/routes/leveret/grok/grok.ts | 54 --- src/routes/leveret/leveret.ts | 18 - src/routes/members.ts | 19 ++ src/routes/oredic.ts | 20 ++ src/routes/util/oredic/oredic-ae2.ts | 26 -- src/routes/util/util.ts | 15 - src/server.ts | 5 +- src/services/ai/GrokService.ts | 44 +++ src/services/members/MembersService.ts | 32 ++ src/services/oredic/OredicService.ts | 58 ++++ src/types/{grok.ts => ai.ts} | 6 +- src/types/express.ts | 54 +++ src/types/parsing.ts | 6 +- src/types/server.ts | 13 +- src/types/timer.ts | 9 + src/utils/Endpoint.ts | 51 --- src/utils/Logger.ts | 8 +- src/utils/Timer.ts | 8 +- src/utils/grok/formatter.ts | 25 +- src/utils/grok/grok.ts | 2 +- src/utils/parentClasses/ErrorProne.ts | 8 +- src/utils/parsers/OredicBuilder.ts | 161 +++++++++ src/utils/parsers/OredicMatcher.ts | 90 +++-- src/utils/parsers/OredicShortener.ts | 307 +++++++----------- storage/oredic/dumps/nomi-ceu.txt | 1 - 55 files changed, 1109 insertions(+), 947 deletions(-) create mode 100644 src/config/routes.ts create mode 100644 src/controllers/AiController.ts delete mode 100644 src/controllers/GrokController.ts create mode 100644 src/middleware/analytics/perf/timer.ts create mode 100644 src/middleware/auth/globalRateLimits.ts rename src/middleware/{ => auth}/leveretAuth.ts (89%) create mode 100644 src/middleware/auth/userRateLimits.ts delete mode 100644 src/middleware/cleanup.ts delete mode 100644 src/middleware/endpointData.ts create mode 100644 src/middleware/endpoints/aiEndpoint.ts create mode 100644 src/middleware/endpoints/loadEndpoint.ts create mode 100644 src/middleware/endpoints/membersEndpoint.ts create mode 100644 src/middleware/endpoints/oredicEndpoint.ts delete mode 100644 src/middleware/globalRateLimits.ts create mode 100644 src/middleware/sanity/aiTokenChecker.ts create mode 100644 src/middleware/sanity/cleanup.ts rename src/middleware/{ => sanity}/filterBody.ts (80%) rename src/middleware/{ => sanity}/jsonWithRawBody.ts (100%) rename src/middleware/{ => sanity}/validator.ts (93%) delete mode 100644 src/middleware/setEndpointData.ts delete mode 100644 src/middleware/tokenChecker.ts delete mode 100644 src/middleware/userRateLimits.ts create mode 100644 src/routes/ai.ts delete mode 100644 src/routes/data/data.ts delete mode 100644 src/routes/data/members/members.ts delete mode 100644 src/routes/leveret/grok/grok.ts delete mode 100644 src/routes/leveret/leveret.ts create mode 100644 src/routes/members.ts create mode 100644 src/routes/oredic.ts delete mode 100644 src/routes/util/oredic/oredic-ae2.ts delete mode 100644 src/routes/util/util.ts create mode 100644 src/services/ai/GrokService.ts create mode 100644 src/services/members/MembersService.ts create mode 100644 src/services/oredic/OredicService.ts rename src/types/{grok.ts => ai.ts} (77%) create mode 100644 src/types/express.ts create mode 100644 src/types/timer.ts delete mode 100644 src/utils/Endpoint.ts create mode 100644 src/utils/parsers/OredicBuilder.ts diff --git a/src/bot/EventHandler.ts b/src/bot/EventHandler.ts index f7b53c4..6d60ca0 100644 --- a/src/bot/EventHandler.ts +++ b/src/bot/EventHandler.ts @@ -49,7 +49,7 @@ export class BotEventHandler { const memberId = member.id; this.dbHandler.addToHog(memberId); getLogger().simpleLog( - "info", + "telemetry", `${await findDcUsernameById(memberId)} joined ${ config.DISCORD_SERVER_NAME }` diff --git a/src/config/config.ts b/src/config/config.ts index d4b7bd6..63d2c67 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -9,7 +9,7 @@ import { } from "./validate"; import { RAW_CONFIG, RAW_FILTERS_CONFIG } from "../loaders/storage"; -dotenv.config(); +dotenv.config({ quiet: true }); export const env: Env = validateEnvs(); export const config: Config = validateConfigs(RAW_CONFIG); diff --git a/src/config/routes.ts b/src/config/routes.ts new file mode 100644 index 0000000..13cb998 --- /dev/null +++ b/src/config/routes.ts @@ -0,0 +1,11 @@ +export const AI_MODELS = ["grok", "premium-grok"] as const; +export type AiModel = (typeof AI_MODELS)[number]; + +export const SYSTEM_PROMPTS = ["default", "hogichan", "nomicord"] as const; +export type SystemPrompt = (typeof SYSTEM_PROMPTS)[number]; + +export const OREDIC_PACKS = ["nomi-ceu"] as const; +export type OredicPack = (typeof OREDIC_PACKS)[number]; + +export const OREDIC_ACTIONS = ["simplify", "match", "parse"] as const; +export type OredicAction = (typeof OREDIC_ACTIONS)[number]; diff --git a/src/controllers/AiController.ts b/src/controllers/AiController.ts new file mode 100644 index 0000000..77da215 --- /dev/null +++ b/src/controllers/AiController.ts @@ -0,0 +1,27 @@ +import { Request, Response, NextFunction } from "express"; +import { GrokService } from "../services/ai/GrokService"; + +export class AiController { + private service = new GrokService(); + + prompt = async (req: Request, res: Response, next: NextFunction) => { + const { model, systemPrompt } = req.ai || {}; + + if (!model || !systemPrompt) { + return next(new Error("AI model or system prompt is missing")); + } + + try { + const completion = await this.service.generateCompletion( + req.body, + model, + systemPrompt + ); + + res.data = completion; + next(); + } catch (error) { + next(error); + } + }; +} diff --git a/src/controllers/GrokController.ts b/src/controllers/GrokController.ts deleted file mode 100644 index cb22cb5..0000000 --- a/src/controllers/GrokController.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { NextFunction, Request, RequestHandler, Response } from "express"; -import { getGrokClient } from "../clients/GrokClient"; -import { - DEFAULT_PROMPT, - HOGICHAN_PROMPT, - NOMICORD_PROMPT, -} from "../loaders/storage"; -import { GrokInputData, SystemPromptChoice } from "../types/grok"; -import { formatCompletion } from "../utils/grok/formatter"; -import { getLogger } from "../utils/Logger"; -import { startTimer, stopTimer } from "../utils/Timer"; - -export class GrokController { - constructor() {} - - static systemPromptMappings = { - default: DEFAULT_PROMPT, - hogichan: HOGICHAN_PROMPT, - nomicord: NOMICORD_PROMPT, - }; - - async answerQuestionGeneric( - reqBody: GrokInputData, - model: string, - type: SystemPromptChoice, - endpointName: string - ) { - return await getGrokClient().client.chat.completions.create( - await formatCompletion(reqBody, model, type, endpointName) - ); - } - - handler = - (): RequestHandler => - async (req: Request, res: Response, next: NextFunction) => { - const reqBody = req.body; - const endpointConfig = req.endpointConfig?.config; - - if (!reqBody) { - next(new Error("Request body is missing")); - return; - } - - if (!endpointConfig) { - next(new Error("Endpoint configuration is missing")); - return; - } - - const model = endpointConfig.model; - const systemPrompt = endpointConfig.systemPrompt; - const endpointName = endpointConfig.endpointName; - - try { - startTimer("grok-req"); - const completion = await this.answerQuestionGeneric( - reqBody, - model, - systemPrompt, - endpointName - ); - - const time_taken = stopTimer("grok-req").getTime("ms", 0); - getLogger().simpleLog( - "info", - `Served Request on ${endpointConfig.endpointName} in ${time_taken.formatted}` - ); - - res.json({ completion: completion, duration: time_taken.adjusted }); - } catch (err) { - next(err); - } - }; -} diff --git a/src/controllers/MembersController.ts b/src/controllers/MembersController.ts index 6c5136a..cd20c9c 100644 --- a/src/controllers/MembersController.ts +++ b/src/controllers/MembersController.ts @@ -1,81 +1,36 @@ -import { RequestHandler, Request, Response, NextFunction } from "express"; -import { getDbHandler } from "../db/DbHandler"; -import { findDcUsernameById } from "../utils/bot/usernames"; -import { startTimer, stopTimer } from "../utils/Timer"; -import { getLogger } from "../utils/Logger"; +import { Request, Response, NextFunction } from "express"; +import { MembersService } from "../services/members/MembersService"; export class MembersController { - dbHandler; - - constructor() { - this.dbHandler = getDbHandler(); - } - - async getIds() { - return await this.dbHandler.getHogMembers(); - } - - async getUsernames() { - const idsList = await this.dbHandler.getHogMembers(); - const usernamesList = await Promise.all( - idsList.map(async (memberId) => { - return await findDcUsernameById(memberId); - }) - ); - return usernamesList; - } - - async getUsernamesAndIds() { - const idsList = await this.dbHandler.getHogMembers(); - const idsToUsernames: Record = {}; - - await Promise.all( - idsList.map(async (element) => { - const username = await findDcUsernameById(element); - if (username) { - idsToUsernames[element] = username; - } else { - idsToUsernames[element] = "unknown"; - } - }) - ); - - return idsToUsernames; - } - - handler = - (): RequestHandler => - async (req: Request, res: Response, next: NextFunction) => { - const endpointConfig = req.endpointConfig; - - if (!endpointConfig) { - next(new Error("Endpoint configuration is missing")); - return; - } - const toFetch = endpointConfig.child; - - let members: any; - startTimer("members-fetch"); - try { - switch (toFetch) { - case "ids": - members = await this.getIds(); - break; - case "usernames": - members = await this.getUsernames(); - break; - case "usernames-and-ids": - members = await this.getUsernamesAndIds(); - break; - } - const time_taken_ms = stopTimer("members-fetch").getTime("ms", 0); - getLogger().simpleLog( - "info", - `Served Request on ${endpointConfig.main} in ${time_taken_ms.formatted}` - ); - res.json({ members: members, duration: time_taken_ms.adjusted }); - } catch (err) { - next(err); - } - }; + private service = new MembersService(); + + getIds = async (req: Request, res: Response, next: NextFunction) => { + try { + const members = await this.service.getIds(); + res.data = { ids: members }; + next(); + } catch (error) { + next(error); + } + }; + + getUsernames = async (req: Request, res: Response, next: NextFunction) => { + try { + const members = await this.service.getUsernames(); + res.data = { usernames: members }; + next(); + } catch (error) { + next(error); + } + }; + + getUsers = async (req: Request, res: Response, next: NextFunction) => { + try { + const members = await this.service.getUsers(); + res.data = { users: members }; + next(); + } catch (error) { + next(error); + } + }; } diff --git a/src/controllers/OredicController.ts b/src/controllers/OredicController.ts index 4150ac7..1cee79a 100644 --- a/src/controllers/OredicController.ts +++ b/src/controllers/OredicController.ts @@ -1,29 +1,22 @@ -import { RequestHandler, Request, Response, NextFunction } from "express"; -import { OredicParser } from "../utils/parsers/OredicParser"; -import { OredicMatcher } from "../utils/parsers/OredicMatcher"; +import { Request, Response, NextFunction } from "express"; +import { OredicService } from "../services/oredic/OredicService"; export class OredicController { - constructor() {} + private service = new OredicService(); - async answer(req: Request) { - const Parser = new OredicParser(); - const rules = Parser.parse(req.body.string); - if (!rules) { - // Catch unknown error - return; - } - if (rules.type === "error") { - // Catch error according to the metadata - return; - } + simplify = async (req: Request, res: Response, next: NextFunction) => { + const { pack, action } = req.oredic || {}; - const Matcher = new OredicMatcher(rules, "nomi-ceu"); - return Matcher.match(); - } + if (!pack || !action) { + return next(new Error("Pack or action is missing")); + } - handler = - (): RequestHandler => - async (req: Request, res: Response, next: NextFunction) => { - res.json({ ast: await this.answer(req) }); - }; + try { + const result = await this.service.simplify(req.body.string, pack); + res.data = { ast: result.data }; + next(); + } catch (error) { + next(error); + } + }; } diff --git a/src/middleware/analytics/perf/timer.ts b/src/middleware/analytics/perf/timer.ts new file mode 100644 index 0000000..23d17fa --- /dev/null +++ b/src/middleware/analytics/perf/timer.ts @@ -0,0 +1,23 @@ +import { Request, Response, NextFunction } from "express"; +import { startTimer, stopTimer } from "../../../utils/Timer"; +import { getLogger } from "../../../utils/Logger"; + +export const timerStart = (req: Request, res: Response, next: NextFunction) => { + req.timerId = `req-${Date.now()}`; + startTimer(req.timerId); + + next(); +}; + +export const timerStop = (req: Request, res: Response, next: NextFunction) => { + const timing = stopTimer(req.timerId).getTime("ms", 0); + + const endpoint = req.endpoint || "unknown"; + getLogger().simpleLog( + "telemetry", + `Served ${endpoint} in ${timing.formatted}` + ); + + res.timing = timing; + next(); +}; diff --git a/src/middleware/auth/globalRateLimits.ts b/src/middleware/auth/globalRateLimits.ts new file mode 100644 index 0000000..c950e09 --- /dev/null +++ b/src/middleware/auth/globalRateLimits.ts @@ -0,0 +1,49 @@ +import { Request, Response, NextFunction } from "express"; +import { getDbHandler } from "../../db/DbHandler"; +import { getLogger } from "../../utils/Logger"; +import { config } from "../../config/config"; + +export const globalRateLimits = async ( + req: Request, + res: Response, + next: NextFunction +): Promise => { + const aiModel = req.ai?.model; + + if (!aiModel) { + res.status(500).json({ error: "AI model not found" }); + return; + } + + const modelConfig = config.ENDPOINTS[aiModel]; + if (!modelConfig) { + res.status(500).json({ error: "Model configuration not found" }); + return; + } + + const isAllowed = await checkRateLimits( + req.body.userId, + aiModel, + modelConfig.RATE_LIMIT.USER.WHITELIST + ); + if (!isAllowed) { + getLogger().simpleLog("warn", `Global Rate Limit Reached for ${aiModel}`); + res + .status(429) + .json({ error: "You're Being Rate Limited by the Global Rate Limit" }); + return; + } + await getDbHandler().updateGlobalRates(aiModel, "take"); + next(); +}; + +async function checkRateLimits( + dcUserId: string, + modelName: string, + whitelist: string[] +) { + if (whitelist.includes(dcUserId)) return true; + const rates = await getDbHandler().getGlobalRates(modelName); + if (rates === null) return false; + return rates > 0; +} diff --git a/src/middleware/leveretAuth.ts b/src/middleware/auth/leveretAuth.ts similarity index 89% rename from src/middleware/leveretAuth.ts rename to src/middleware/auth/leveretAuth.ts index ee89cd7..cee8c58 100644 --- a/src/middleware/leveretAuth.ts +++ b/src/middleware/auth/leveretAuth.ts @@ -1,8 +1,8 @@ import { Request, Response, NextFunction } from "express"; -import { config } from "../config/config"; +import { config } from "../../config/config"; import { createHash, verify } from "crypto"; -import { LEVERET_PUBLIC_KEY } from "../loaders/keys"; -import { getLogger } from "../utils/Logger"; +import { LEVERET_PUBLIC_KEY } from "../../loaders/keys"; +import { getLogger } from "../../utils/Logger"; export const leveretAuth = ( req: Request, @@ -16,6 +16,8 @@ export const leveretAuth = ( res.status(401).json({ error: "Unauthorized" }); return; } + getLogger().simpleLog("telemetry", "Authorized Leveret Request"); + req.flags = { isLeveret: true }; next(); }; diff --git a/src/middleware/auth/userRateLimits.ts b/src/middleware/auth/userRateLimits.ts new file mode 100644 index 0000000..578fc56 --- /dev/null +++ b/src/middleware/auth/userRateLimits.ts @@ -0,0 +1,52 @@ +import { Request, Response, NextFunction } from "express"; +import { getDbHandler } from "../../db/DbHandler"; +import { getLogger } from "../../utils/Logger"; +import { config } from "../../config/config"; + +export const userRateLimits = async ( + req: Request, + res: Response, + next: NextFunction +): Promise => { + const userId = req.body.userId; + const aiModel = req.ai?.model; + + if (!aiModel) { + res.status(500).json({ error: "AI model not found" }); + return; + } + + const modelConfig = config.ENDPOINTS[aiModel]; + if (!modelConfig) { + res.status(500).json({ error: "Model configuration not found" }); + return; + } + + const isAllowed = await checkRateLimits( + userId, + aiModel, + modelConfig.RATE_LIMIT.USER.WHITELIST + ); + + if (!isAllowed) { + const username = await getDbHandler().getUsername(userId, aiModel); + getLogger().simpleLog( + "warn", + `User: ${username} got rate limited on ${aiModel}` + ); + res.status(429).json({ error: "You're Being Rate Limited" }); + return; + } + + next(); +}; + +async function checkRateLimits( + dcUserId: string, + modelName: string, + whitelist: string[] +) { + if (whitelist.includes(dcUserId)) return true; + const rates = await getDbHandler().getUserRates(dcUserId, modelName); + return rates > 0; +} diff --git a/src/middleware/cleanup.ts b/src/middleware/cleanup.ts deleted file mode 100644 index d2b1421..0000000 --- a/src/middleware/cleanup.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { NextFunction, Response, Request } from "express"; -import { getLogger } from "../utils/Logger"; -import { getDbHandler } from "../db/DbHandler"; - -export const cleanup = async ( - req: Request, - res: Response, - next: NextFunction -): Promise => { - const userId = req.body.userId; - const endpointConfig = req.endpointConfig?.config; - - if (!endpointConfig) { - res.status(500).json({ error: "Endpoint configuration not found" }); - return; - } - - if (req.endpointConfig?.source === "leveret") { - await getDbHandler().updateGlobalRates(endpointConfig.endpointName, "take"); - await getDbHandler().updateUserRates( - userId, - endpointConfig.endpointName, - "take" - ); - } - - getLogger().simpleLog("info", "Authorized Request"); - next(); -}; diff --git a/src/middleware/endpointData.ts b/src/middleware/endpointData.ts deleted file mode 100644 index 29f6eab..0000000 --- a/src/middleware/endpointData.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { SystemPromptChoice } from "../types/grok"; -import { EndpointConfig } from "../types/server"; -import { getEndpointHandler, nullifyEndpointHandler } from "../utils/Endpoint"; -import { config } from "../config/config"; -import { getLogger } from "../utils/Logger"; - -declare global { - namespace Express { - interface Request { - endpointConfig?: EndpointConfig; - } - } -} - -export const endpointData = ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - const endpointHandler = getEndpointHandler(); - const source = endpointHandler.getSource(); - const mainEndpoint = endpointHandler.getMain(); - const child = endpointHandler.getChild(); - - switch (source) { - case "leveret": - let endpointSpecificConfig; - if (mainEndpoint) { - const configEntry = config.ENDPOINTS[mainEndpoint]; - if (configEntry) { - endpointSpecificConfig = { - endpointName: mainEndpoint, - systemPrompt: (child as SystemPromptChoice) || "default", - model: configEntry.MODEL, - maxPromptTokens: configEntry.MAX_PROMPT_TK, - maxContextTokens: configEntry.MAX_CONTEXT_TK, - maxTotalTokens: configEntry.MAX_TOTAL_TK, - filters: configEntry.FILTERS, - rateLimit: configEntry.RATE_LIMIT, - }; - } else { - getLogger().simpleLog( - "warn", - `No config found for endpoint: ${mainEndpoint}` - ); - } - } - - req.endpointConfig = { - source: endpointHandler.getSource(), - main: mainEndpoint, - child: child, - config: endpointSpecificConfig, - }; - break; - - case "data": - req.endpointConfig = { - source: source, - main: mainEndpoint, - child: child, - }; - break; - } - - nullifyEndpointHandler(); - next(); - } catch (err) { - getLogger().simpleLog("error", `endpointData error: ${err}`); - next(err); - } -}; diff --git a/src/middleware/endpoints/aiEndpoint.ts b/src/middleware/endpoints/aiEndpoint.ts new file mode 100644 index 0000000..a24c362 --- /dev/null +++ b/src/middleware/endpoints/aiEndpoint.ts @@ -0,0 +1,44 @@ +import { Request, Response, NextFunction } from "express"; +import { config } from "../../config/config"; +import { + AI_MODELS, + SYSTEM_PROMPTS, + AiModel, + SystemPrompt, +} from "../../config/routes"; +import "../../types/express"; + +export const aiEndpoint = (req: Request, res: Response, next: NextFunction) => { + const { model, systemPrompt } = req.params; + + const isValidModel = AI_MODELS.includes(model as AiModel); + if (!isValidModel) { + res.status(404).json({ + error: "Model not found", + message: `Model must be one of: ${AI_MODELS.join(", ")}`, + }); + return; + } + + const isValidPrompt = SYSTEM_PROMPTS.includes(systemPrompt as SystemPrompt); + if (!isValidPrompt) { + res.status(400).json({ + error: "Invalid system prompt", + message: `System prompt must be one of: ${SYSTEM_PROMPTS.join(", ")}`, + }); + return; + } + + const modelConfig = config.ENDPOINTS[model]; + if (!modelConfig) { + res.status(500).json({ error: "Model configuration missing" }); + return; + } + + req.ai = { + model: model as AiModel, + systemPrompt: systemPrompt as SystemPrompt, + }; + + next(); +}; diff --git a/src/middleware/endpoints/loadEndpoint.ts b/src/middleware/endpoints/loadEndpoint.ts new file mode 100644 index 0000000..12f625e --- /dev/null +++ b/src/middleware/endpoints/loadEndpoint.ts @@ -0,0 +1,12 @@ +import { Request, Response, NextFunction } from "express"; + +export const loadEndpoint = ( + req: Request, + res: Response, + next: NextFunction +) => { + const endpoint = `${req.baseUrl}${req.path}`; + req.endpoint = endpoint; + + next(); +}; diff --git a/src/middleware/endpoints/membersEndpoint.ts b/src/middleware/endpoints/membersEndpoint.ts new file mode 100644 index 0000000..4322d41 --- /dev/null +++ b/src/middleware/endpoints/membersEndpoint.ts @@ -0,0 +1,10 @@ +import { Request, Response, NextFunction } from "express"; +import "../../types/express"; + +export const membersEndpoint = ( + req: Request, + res: Response, + next: NextFunction +) => { + next(); +}; diff --git a/src/middleware/endpoints/oredicEndpoint.ts b/src/middleware/endpoints/oredicEndpoint.ts new file mode 100644 index 0000000..e5001f4 --- /dev/null +++ b/src/middleware/endpoints/oredicEndpoint.ts @@ -0,0 +1,41 @@ +import { Request, Response, NextFunction } from "express"; +import { + OREDIC_PACKS, + OREDIC_ACTIONS, + OredicPack, + OredicAction, +} from "../../config/routes"; +import "../../types/express"; + +export const oredicEndpoint = ( + req: Request, + res: Response, + next: NextFunction +) => { + const { pack, action } = req.params; + + const isValidPack = OREDIC_PACKS.includes(pack as OredicPack); + if (!isValidPack) { + res.status(404).json({ + error: "Invalid pack", + message: `Pack must be one of: ${OREDIC_PACKS.join(", ")}`, + }); + return; + } + + const isValidAction = OREDIC_ACTIONS.includes(action as OredicAction); + if (!isValidAction) { + res.status(404).json({ + error: "Invalid action", + message: `Action must be one of: ${OREDIC_ACTIONS.join(", ")}`, + }); + return; + } + + req.oredic = { + pack: pack as OredicPack, + action: action as OredicAction, + }; + + next(); +}; diff --git a/src/middleware/globalRateLimits.ts b/src/middleware/globalRateLimits.ts deleted file mode 100644 index 983f093..0000000 --- a/src/middleware/globalRateLimits.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { getDbHandler } from "../db/DbHandler"; -import { getLogger } from "../utils/Logger"; - -export const globalRateLimits = async ( - req: Request, - res: Response, - next: NextFunction -): Promise => { - const endpointConfig = req.endpointConfig?.config; - - if (!endpointConfig) { - res.status(500).json({ error: "Endpoint configuration not found" }); - return; - } - - const isAllowed = await checkRateLimits( - req.body.userId, - endpointConfig.endpointName, - endpointConfig.rateLimit.USER.WHITELIST - ); - if (!isAllowed) { - getLogger().simpleLog( - "warn", - `Global Rate Limit Reached for ${endpointConfig.endpointName}` - ); - res - .status(429) - .json({ error: "You're Being Rate Limited by the Global Rate Limit" }); - return; - } - await getDbHandler().updateGlobalRates(endpointConfig.endpointName, "take"); - next(); -}; - -async function checkRateLimits( - dcUserId: string, - endpointName: string, - whitelist: string[] -) { - if (whitelist.includes(dcUserId)) return true; - const rates = await getDbHandler().getGlobalRates(endpointName); - if (rates === null) return false; - return rates > 0; -} diff --git a/src/middleware/sanity/aiTokenChecker.ts b/src/middleware/sanity/aiTokenChecker.ts new file mode 100644 index 0000000..11dcdba --- /dev/null +++ b/src/middleware/sanity/aiTokenChecker.ts @@ -0,0 +1,65 @@ +import { Request, Response, NextFunction } from "express"; +import { getLogger } from "../../utils/Logger"; +import { tokenizeGrok } from "../../utils/grok/grok"; +import { findDcUsernameById } from "../../utils/bot/usernames"; +import { config } from "../../config/config"; + +export const aiTokenChecker = async ( + req: Request, + res: Response, + next: NextFunction +): Promise => { + const aiModel = req.ai?.model; + + if (!aiModel) { + res.status(500).json({ error: "AI model not found" }); + return; + } + + const modelConfig = config.ENDPOINTS[aiModel]; + if (!modelConfig) { + res.status(500).json({ error: "Model configuration not found" }); + return; + } + + // TODO: Implement model-specific tokenization based on aiModel + // For now, using Grok tokenizer as default + const tokenizedPrompt = await tokenizeGrok( + req.body.prompt, + modelConfig.MODEL + ); + const tokenizedContext = await tokenizeGrok( + req.body.context, + modelConfig.MODEL + ); + + if (!tokenizedPrompt || !tokenizedContext) { + res.status(502).json({ error: "AI Tokenizer errored" }); + return; + } + + if (tokenizedPrompt.tokenCount > modelConfig.MAX_PROMPT_TK) { + res + .status(403) + .json({ error: "Token count exceeds the maximum allowed limit" }); + return; + } + + if (tokenizedContext.tokenCount > modelConfig.MAX_CONTEXT_TK) { + res + .status(403) + .json({ error: "Context count exceeds the maximum allowed limit" }); + return; + } + + const totalTokens = tokenizedPrompt.tokenCount + tokenizedContext.tokenCount; + const username = await findDcUsernameById(req.body.userId); + getLogger().simpleLog( + "telemetry", + `${username} passed a request for ${totalTokens} tokens on ${aiModel}` + ); + next(); +}; + +// Legacy export for backwards compatibility +export const tokenCheckerGrok = aiTokenChecker; diff --git a/src/middleware/sanity/cleanup.ts b/src/middleware/sanity/cleanup.ts new file mode 100644 index 0000000..eeee7fd --- /dev/null +++ b/src/middleware/sanity/cleanup.ts @@ -0,0 +1,26 @@ +import { NextFunction, Response, Request } from "express"; +import { getDbHandler } from "../../db/DbHandler"; +import { getLogger } from "../../utils/Logger"; + +export const cleanup = async ( + req: Request, + res: Response, + next: NextFunction +): Promise => { + const isLeveret = req.flags?.isLeveret; + + if (isLeveret) { + const userId = req.body.userId; + const aiModel = req.ai?.model; + + if (aiModel) { + await getDbHandler().updateGlobalRates(aiModel, "take"); + await getDbHandler().updateUserRates(userId, aiModel, "take"); + } + } + + res.json({ + ...res.data, + timing: res.timing, + }); +}; diff --git a/src/middleware/filterBody.ts b/src/middleware/sanity/filterBody.ts similarity index 80% rename from src/middleware/filterBody.ts rename to src/middleware/sanity/filterBody.ts index 1201734..5632f7f 100644 --- a/src/middleware/filterBody.ts +++ b/src/middleware/sanity/filterBody.ts @@ -1,8 +1,8 @@ import { Request, Response, NextFunction } from "express"; -import { filtersConfig } from "../config/config"; -import { FILTERS } from "../loaders/filters"; -import { getLogger } from "../utils/Logger"; -import { getDbHandler } from "../db/DbHandler"; +import { filtersConfig } from "../../config/config"; +import { FILTERS } from "../../loaders/filters"; +import { getLogger } from "../../utils/Logger"; +import { getDbHandler } from "../../db/DbHandler"; //TODO: Make the config a map for Severity -> Action, and handle that. export const filterBody = async ( @@ -12,16 +12,14 @@ export const filterBody = async ( ): Promise => { const prompt = req.body.prompt; const context = req.body.context; - const endpointConfig = req.endpointConfig?.config; - const endpointName = endpointConfig?.endpointName; - if (!endpointName) { - res.status(500).json("no endpoint name, this shouldn't ever happen..."); + const aiModel = req.ai?.model; + + if (!aiModel) { + res.status(500).json("AI model not found"); return; } - const username = await getDbHandler().getUsername( - req.body.userId, - endpointName - ); + + const username = await getDbHandler().getUsername(req.body.userId, aiModel); switch (filtersConfig.grok.filterAction) { case "warn": diff --git a/src/middleware/jsonWithRawBody.ts b/src/middleware/sanity/jsonWithRawBody.ts similarity index 100% rename from src/middleware/jsonWithRawBody.ts rename to src/middleware/sanity/jsonWithRawBody.ts diff --git a/src/middleware/validator.ts b/src/middleware/sanity/validator.ts similarity index 93% rename from src/middleware/validator.ts rename to src/middleware/sanity/validator.ts index b0c0856..415b8e4 100644 --- a/src/middleware/validator.ts +++ b/src/middleware/sanity/validator.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { ZodError, ZodObject } from "zod"; -import { getLogger } from "../utils/Logger"; +import { getLogger } from "../../utils/Logger"; export const zodValidator = (schema: ZodObject) => { return (req: Request, res: Response, next: NextFunction) => { diff --git a/src/middleware/setEndpointData.ts b/src/middleware/setEndpointData.ts deleted file mode 100644 index a0af6a5..0000000 --- a/src/middleware/setEndpointData.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { - getEndpointHandler, - initEndpointHandler, - nullifyEndpointHandler, -} from "../utils/Endpoint"; -import { EndpointLevel } from "../types/server"; -import { getLogger } from "../utils/Logger"; - -export const setEndpointData = (level: EndpointLevel, data: string) => { - return (req: Request, res: Response, next: NextFunction) => { - try { - switch (level) { - case "source": - initEndpointHandler(data); - break; - - case "main": - getEndpointHandler().setMain(data); - break; - - case "child": - getEndpointHandler().setChild(data); - break; - } - next(); - } catch (err) { - getLogger().simpleLog("error", `setEndpointData error: ${err}`); - res - .status(500) - .json({ - error: "Internal Server Error - Endpoint configuration failed", - }); - } - }; -}; diff --git a/src/middleware/tokenChecker.ts b/src/middleware/tokenChecker.ts deleted file mode 100644 index df3fa77..0000000 --- a/src/middleware/tokenChecker.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { getLogger } from "../utils/Logger"; -import { tokenizeGrok } from "../utils/grok/grok"; -import { findDcUsernameById } from "../utils/bot/usernames"; - -export const tokenCheckerGrok = async ( - req: Request, - res: Response, - next: NextFunction -): Promise => { - const endpointConfig = req.endpointConfig?.config; - - if (!endpointConfig) { - res.status(500).json({ error: "Endpoint configuration not found" }); - return; - } - - const tokenizedPrompt = await tokenizeGrok( - req.body.prompt, - endpointConfig.model - ); - const tokenizedContext = await tokenizeGrok( - req.body.context, - endpointConfig.model - ); - - if (!tokenizedPrompt || !tokenizedContext) { - res.status(502).json("Grok Tokenizer Errored"); - return; - } - - if (tokenizedPrompt.tokenCount > endpointConfig.maxPromptTokens) { - res.status(403).json("Token count exceeds the maximum allowed limit"); - return; - } - - if (tokenizedContext.tokenCount > endpointConfig.maxContextTokens) { - res.status(403).json("Context count exceeds the maximum allowed limit"); - return; - } - - getLogger().simpleLog( - "info", - `${await findDcUsernameById(req.body.userId)} passed a request for ${ - tokenizedPrompt.tokenCount + tokenizedContext.tokenCount - } tokens on ${endpointConfig.endpointName}` - ); - next(); -}; diff --git a/src/middleware/userRateLimits.ts b/src/middleware/userRateLimits.ts deleted file mode 100644 index 73e98db..0000000 --- a/src/middleware/userRateLimits.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { getDbHandler } from "../db/DbHandler"; -import { getLogger } from "../utils/Logger"; - -export const userRateLimits = async ( - req: Request, - res: Response, - next: NextFunction -): Promise => { - const userId = req.body.userId; - const endpointConfig = req.endpointConfig?.config; - - if (!endpointConfig) { - res.status(500).json({ error: "Endpoint configuration not found" }); - return; - } - - const isAllowed = await checkRateLimits( - userId, - endpointConfig.endpointName, - endpointConfig.rateLimit.USER.WHITELIST - ); - - if (!isAllowed) { - const username = await getDbHandler().getUsername( - userId, - endpointConfig.endpointName - ); - getLogger().simpleLog( - "warn", - `User: ${username} got rate limited on ${endpointConfig.endpointName}` - ); - res.status(429).json({ error: "You're Being Rate Limited" }); - return; - } - - next(); -}; - -async function checkRateLimits( - dcUserId: string, - endpointName: string, - whitelist: string[] -) { - if (whitelist.includes(dcUserId)) return true; - const rates = await getDbHandler().getUserRates(dcUserId, endpointName); - return rates > 0; -} diff --git a/src/routes/ai.ts b/src/routes/ai.ts new file mode 100644 index 0000000..dad7029 --- /dev/null +++ b/src/routes/ai.ts @@ -0,0 +1,37 @@ +import { Router } from "express"; +import { AiController } from "../controllers/AiController"; +import { aiEndpoint } from "../middleware/endpoints/aiEndpoint"; +import { globalRateLimits } from "../middleware/auth/globalRateLimits"; +import { zodValidator } from "../middleware/sanity/validator"; +import { AiInputDataSchema } from "../types/ai"; +import { cleanup } from "../middleware/sanity/cleanup"; +import { aiTokenChecker } from "../middleware/sanity/aiTokenChecker"; +import { filterBody } from "../middleware/sanity/filterBody"; +import { leveretAuth } from "../middleware/auth/leveretAuth"; +import { jsonWithRawBody } from "../middleware/sanity/jsonWithRawBody"; +import { userRateLimits } from "../middleware/auth/userRateLimits"; +import { timerStop } from "../middleware/analytics/perf/timer"; + +export function aiRoutes() { + const router = Router(); + const controller = new AiController(); + + router.use(jsonWithRawBody()); + router.use(leveretAuth); + + router.post( + "/:model/:systemPrompt", + aiEndpoint, + zodValidator(AiInputDataSchema), + globalRateLimits, + userRateLimits, + filterBody, + aiTokenChecker, + controller.prompt + ); + + router.use(timerStop); + router.use(cleanup); + + return router; +} diff --git a/src/routes/data/data.ts b/src/routes/data/data.ts deleted file mode 100644 index 8bbc127..0000000 --- a/src/routes/data/data.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Router } from "express"; -import { setEndpointData } from "../../middleware/setEndpointData"; -import { membersRoutes } from "./members/members"; - -export function dataRoutes() { - const router = Router(); - - router.use("/members", setEndpointData("main", "members"), membersRoutes()); - - return router; -} diff --git a/src/routes/data/members/members.ts b/src/routes/data/members/members.ts deleted file mode 100644 index cb8b8e8..0000000 --- a/src/routes/data/members/members.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Router } from "express"; -import { cleanup } from "../../../middleware/cleanup"; -import { endpointData } from "../../../middleware/endpointData"; -import { setEndpointData } from "../../../middleware/setEndpointData"; -import { MembersController } from "../../../controllers/MembersController"; - -export function membersRoutes() { - const router = Router(); - const controller = new MembersController(); - - router.get( - "/ids", - setEndpointData("child", "ids"), - endpointData, - cleanup, - controller.handler() - ); - router.get( - "/usernames", - setEndpointData("child", "usernames"), - endpointData, - cleanup, - controller.handler() - ); - router.get( - "/usernames-and-ids", - setEndpointData("child", "usernames-and-ids"), - endpointData, - cleanup, - controller.handler() - ); - - return router; -} diff --git a/src/routes/index.ts b/src/routes/index.ts index 5af84fd..4778403 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,30 +1,20 @@ import { Router } from "express"; -import { grokRoutes } from "./leveret/grok/grok"; -import { leveretRoutes } from "./leveret/leveret"; -import { leveretAuth } from "../middleware/leveretAuth"; -import { jsonWithRawBody } from "../middleware/jsonWithRawBody"; -import { initEndpointHandler } from "../utils/Endpoint"; -import { setEndpointData } from "../middleware/setEndpointData"; -import { dataRoutes } from "./data/data"; -import { utilRoutes } from "./util/util"; +import { timerStart, timerStop } from "../middleware/analytics/perf/timer"; +import { cleanup } from "../middleware/sanity/cleanup"; +import { aiRoutes } from "./ai"; +import { membersRoutes } from "./members"; +import { oredicRoutes } from "./oredic"; +import { loadEndpoint } from "../middleware/endpoints/loadEndpoint"; export function routes() { const router = Router(); - router.use( - "/leveret", - jsonWithRawBody(), - setEndpointData("source", "leveret"), - leveretAuth, - leveretRoutes() - ); - router.use("/data", setEndpointData("source", "data"), dataRoutes()); + router.use(timerStart); + router.use(loadEndpoint); + + router.use("/ai", aiRoutes()); + router.use("/members", membersRoutes()); + router.use("/oredic", oredicRoutes()); - router.use( - "/util", - jsonWithRawBody(), - setEndpointData("source", "util"), - utilRoutes() - ); return router; } diff --git a/src/routes/leveret/grok/grok.ts b/src/routes/leveret/grok/grok.ts deleted file mode 100644 index 7935922..0000000 --- a/src/routes/leveret/grok/grok.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Router } from "express"; -import { GrokController } from "../../../controllers/GrokController"; -import { userRateLimits } from "../../../middleware/userRateLimits"; -import { globalRateLimits } from "../../../middleware/globalRateLimits"; -import { zodValidator } from "../../../middleware/validator"; -import { GrokInputDataSchema } from "../../../types/grok"; -import { cleanup } from "../../../middleware/cleanup"; -import { tokenCheckerGrok } from "../../../middleware/tokenChecker"; -import { setEndpointData } from "../../../middleware/setEndpointData"; -import { endpointData } from "../../../middleware/endpointData"; -import { filterBody } from "../../../middleware/filterBody"; - -export function grokRoutes() { - const router = Router(); - const controller = new GrokController(); - - router.post( - "/simple", - setEndpointData("child", "simple"), - endpointData, - zodValidator(GrokInputDataSchema), - globalRateLimits, - userRateLimits, - filterBody, - tokenCheckerGrok, - cleanup, - controller.handler() - ); - router.post( - "/hogichan", - setEndpointData("child", "hogichan"), - endpointData, - zodValidator(GrokInputDataSchema), - globalRateLimits, - userRateLimits, - filterBody, - tokenCheckerGrok, - cleanup, - controller.handler() - ); - router.post( - "/nomicord", - setEndpointData("child", "nomicord"), - endpointData, - zodValidator(GrokInputDataSchema), - globalRateLimits, - filterBody, - tokenCheckerGrok, - cleanup, - controller.handler() - ); - - return router; -} diff --git a/src/routes/leveret/leveret.ts b/src/routes/leveret/leveret.ts deleted file mode 100644 index f37734b..0000000 --- a/src/routes/leveret/leveret.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Router } from "express"; -import { grokRoutes } from "./grok/grok"; -import { setEndpointData } from "../../middleware/setEndpointData"; -import { oredicRoutes } from "../util/oredic/oredic-ae2"; - -export function leveretRoutes() { - const router = Router(); - - router.use("/grok", setEndpointData("main", "grok"), grokRoutes()); - - router.use( - "/premium-grok", - setEndpointData("main", "premium-grok"), - grokRoutes() - ); - - return router; -} diff --git a/src/routes/members.ts b/src/routes/members.ts new file mode 100644 index 0000000..f450f5f --- /dev/null +++ b/src/routes/members.ts @@ -0,0 +1,19 @@ +import { Router } from "express"; +import { MembersController } from "../controllers/MembersController"; +import { membersEndpoint } from "../middleware/endpoints/membersEndpoint"; +import { cleanup } from "../middleware/sanity/cleanup"; +import { timerStop } from "../middleware/analytics/perf/timer"; + +export function membersRoutes() { + const router = Router(); + const controller = new MembersController(); + + router.get("/user-ids", membersEndpoint, controller.getIds); + router.get("/usernames", membersEndpoint, controller.getUsernames); + router.get("/users", membersEndpoint, controller.getUsers); + + router.use(timerStop); + router.use(cleanup); + + return router; +} diff --git a/src/routes/oredic.ts b/src/routes/oredic.ts new file mode 100644 index 0000000..45cc893 --- /dev/null +++ b/src/routes/oredic.ts @@ -0,0 +1,20 @@ +import { Router } from "express"; +import { OredicController } from "../controllers/OredicController"; +import { oredicEndpoint } from "../middleware/endpoints/oredicEndpoint"; +import { jsonWithRawBody } from "../middleware/sanity/jsonWithRawBody"; +import { cleanup } from "../middleware/sanity/cleanup"; +import { timerStop } from "../middleware/analytics/perf/timer"; + +export function oredicRoutes() { + const router = Router(); + const controller = new OredicController(); + + router.use(jsonWithRawBody()); + + router.post("/:pack/:action", oredicEndpoint, controller.simplify); + + router.use(timerStop); + router.use(cleanup); + + return router; +} diff --git a/src/routes/util/oredic/oredic-ae2.ts b/src/routes/util/oredic/oredic-ae2.ts deleted file mode 100644 index 36c93be..0000000 --- a/src/routes/util/oredic/oredic-ae2.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Router } from "express"; -import { GrokController } from "../../../controllers/GrokController"; -import { cleanup } from "../../../middleware/cleanup"; -import { endpointData } from "../../../middleware/endpointData"; -import { filterBody } from "../../../middleware/filterBody"; -import { globalRateLimits } from "../../../middleware/globalRateLimits"; -import { setEndpointData } from "../../../middleware/setEndpointData"; -import { tokenCheckerGrok } from "../../../middleware/tokenChecker"; -import { userRateLimits } from "../../../middleware/userRateLimits"; -import { zodValidator } from "../../../middleware/validator"; -import { GrokInputDataSchema } from "../../../types/grok"; -import { OredicController } from "../../../controllers/OredicController"; - -export function oredicRoutes() { - const router = Router(); - const controller = new OredicController(); - - router.post( - "/nomi-ceu", - setEndpointData("child", "nomi-ceu"), - endpointData, - controller.handler() - ); - - return router; -} diff --git a/src/routes/util/util.ts b/src/routes/util/util.ts deleted file mode 100644 index 15d81b7..0000000 --- a/src/routes/util/util.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Router } from "express"; -import { setEndpointData } from "../../middleware/setEndpointData"; -import { oredicRoutes } from "./oredic/oredic-ae2"; - -export function utilRoutes() { - const router = Router(); - - router.use( - "/oredic-ae2", - setEndpointData("main", "oredic-ae2"), - oredicRoutes() - ); - - return router; -} diff --git a/src/server.ts b/src/server.ts index fafe8b9..0ce0f01 100644 --- a/src/server.ts +++ b/src/server.ts @@ -38,15 +38,16 @@ async function main() { const expressApp = initExpress(); expressApp.listen(config.PORT, config.RUNNING_IP, () => { - getLogger().formattingLog("Server Ready"); + getLogger().formattingLog("Server Info"); getLogger().simpleLog( "info", `Server is running at http://${config.RUNNING_IP}:${config.PORT}` ); getLogger().simpleLog( - "info", + "telemetry", `Server took ${stopTimer("main").getTime("auto", 3).formatted} to start` ); + getLogger().formattingLog("Server Ready"); }); } diff --git a/src/services/ai/GrokService.ts b/src/services/ai/GrokService.ts new file mode 100644 index 0000000..8a11929 --- /dev/null +++ b/src/services/ai/GrokService.ts @@ -0,0 +1,44 @@ +import { getGrokClient } from "../../clients/GrokClient"; +import { config } from "../../config/config"; +import { AiModel, SystemPrompt } from "../../config/routes"; +import { AiInputData } from "../../types/ai"; +import { + DEFAULT_PROMPT, + HOGICHAN_PROMPT, + NOMICORD_PROMPT, +} from "../../loaders/storage"; +import { formatCompletion } from "../../utils/grok/formatter"; + +const SYSTEM_PROMPTS = { + default: DEFAULT_PROMPT, + hogichan: HOGICHAN_PROMPT, + nomicord: NOMICORD_PROMPT, +} as const; + +export class GrokService { + async generateCompletion( + input: AiInputData, + model: AiModel, + systemPrompt: SystemPrompt + ) { + const modelConfig = config.ENDPOINTS[model]; + if (!modelConfig) { + throw new Error(`Model configuration not found for ${model}`); + } + + const completionParams = await formatCompletion( + input, + modelConfig.MODEL, + systemPrompt, + model + ); + + return await getGrokClient().client.chat.completions.create( + completionParams + ); + } + + getSystemPrompt(type: SystemPrompt): string { + return SYSTEM_PROMPTS[type]; + } +} diff --git a/src/services/members/MembersService.ts b/src/services/members/MembersService.ts new file mode 100644 index 0000000..63faea8 --- /dev/null +++ b/src/services/members/MembersService.ts @@ -0,0 +1,32 @@ +import { getDbHandler } from "../../db/DbHandler"; +import { findDcUsernameById } from "../../utils/bot/usernames"; + +export class MembersService { + private db = getDbHandler(); + + async getIds(): Promise { + return this.db.getHogMembers(); + } + + async getUsernames(): Promise { + const ids = await this.db.getHogMembers(); + const usernames = await Promise.all( + ids.map((id) => findDcUsernameById(id)) + ); + return usernames.filter((name): name is string => name !== null); + } + + async getUsers(): Promise> { + const ids = await this.db.getHogMembers(); + const result: Record = {}; + + await Promise.all( + ids.map(async (id) => { + const username = await findDcUsernameById(id); + result[id] = username || "unknown"; + }) + ); + + return result; + } +} diff --git a/src/services/oredic/OredicService.ts b/src/services/oredic/OredicService.ts new file mode 100644 index 0000000..f37ebdb --- /dev/null +++ b/src/services/oredic/OredicService.ts @@ -0,0 +1,58 @@ +import { OredicParser } from "../../utils/parsers/OredicParser"; +import { OredicMatcher } from "../../utils/parsers/OredicMatcher"; +import { OredicPack } from "../../config/routes"; +import { Ast } from "../../types/parsing"; +import { StandardError } from "../../types/errors"; + +type OredicResult = { + type: "success" | "error"; + data?: any; + error?: string; +}; + +export class OredicService { + private parser = new OredicParser(); + + async simplify(input: string, pack: OredicPack): Promise { + const parseResult = this.parse(input); + + if (!parseResult) { + return { + type: "error", + error: "Failed to parse input", + }; + } + + if ((parseResult as StandardError).type === "error") { + const error = parseResult as StandardError; + return { + type: "error", + error: error.message || "Parse error", + }; + } + + try { + const ast = parseResult as Ast; + const result = this.match(ast, pack); + return { + type: "success", + data: result, + }; + } catch (error) { + return { + type: "error", + error: error instanceof Error ? error.message : "Unknown error", + }; + } + } + + private parse(input: string): Ast | StandardError { + return this.parser.parse(input); + } + + private match(ast: Ast, pack: OredicPack) { + if (!ast) throw new Error("AST is null"); + const matcher = new OredicMatcher(pack); + return matcher.match(ast); + } +} diff --git a/src/types/grok.ts b/src/types/ai.ts similarity index 77% rename from src/types/grok.ts rename to src/types/ai.ts index c2c7ac5..a900ca8 100644 --- a/src/types/grok.ts +++ b/src/types/ai.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -export const GrokInputDataSchema = z.object({ +export const AiInputDataSchema = z.object({ userId: z.string().min(1, "userId cannot be empty"), prompt: z .string() @@ -9,9 +9,7 @@ export const GrokInputDataSchema = z.object({ context: z.string().max(5000, "context too long"), attachment: z.url("attachment must be a valid URL").nullable(), }); -export type GrokInputData = z.infer; - -export type SystemPromptChoice = "default" | "hogichan" | "nomicord"; +export type AiInputData = z.infer; export type ModelChoice = "grok-3-mini" | "grok-4-0709"; diff --git a/src/types/express.ts b/src/types/express.ts new file mode 100644 index 0000000..fbbe21c --- /dev/null +++ b/src/types/express.ts @@ -0,0 +1,54 @@ +import { + AiModel, + SystemPrompt, + OredicPack, + OredicAction, +} from "../config/routes"; +import { TimerRes } from "./timer"; +import { ChatCompletion } from "openai/resources/index"; +import { exAst } from "./parsing"; + +declare global { + namespace Express { + interface Request { + timerId: string; + endpoint?: string; + flags?: { + isLeveret?: boolean; + }; + ai?: { + model?: AiModel; + systemPrompt?: SystemPrompt; + }; + oredic?: { + pack?: OredicPack; + action?: OredicAction; + }; + } + } +} + +declare global { + namespace Express { + interface Response { + data: AiData | MembersData | OredicData; + timing: TimerRes; + } + } +} + +export type AiData = ChatCompletion; +export type MembersData = { + users?: Record; + ids?: string[]; + usernames?: string[]; +}; +export type OredicData = { + ast?: exAst; + matches?: string[]; + bestFilter?: { + string: string; + }; +}; + +export {}; diff --git a/src/types/parsing.ts b/src/types/parsing.ts index 66f0685..ad9bb04 100644 --- a/src/types/parsing.ts +++ b/src/types/parsing.ts @@ -113,4 +113,8 @@ export type Token = | { type: NodeNames.TEXT; content: string } | { type: NodeNames.WILDCARD }; -export type SupportedPack = "nomi-ceu"; +export type Matches = string[]; + +export type BuildOptions = { + shortenedDisjunction: boolean; +}; diff --git a/src/types/server.ts b/src/types/server.ts index 5787636..2132215 100644 --- a/src/types/server.ts +++ b/src/types/server.ts @@ -1,8 +1,13 @@ -import { SystemPromptChoice } from "./grok"; +import { SystemPrompt } from "../config/routes"; import { EndpointConfig as ConfigEndpointConfig } from "../config/schema"; -import { PathLike } from "fs"; -export type LogType = "success" | "info" | "warn" | "error" | "debug"; +export type LogType = + | "success" + | "info" + | "warn" + | "error" + | "debug" + | "telemetry"; export type Filter = { pattern: RegExp; @@ -27,7 +32,7 @@ export type EndpointConfig = { export type RequestEndpointConfig = { endpointName: string; - systemPrompt: SystemPromptChoice; + systemPrompt: SystemPrompt; model: string; maxPromptTokens: number; maxContextTokens: number; diff --git a/src/types/timer.ts b/src/types/timer.ts new file mode 100644 index 0000000..b9301db --- /dev/null +++ b/src/types/timer.ts @@ -0,0 +1,9 @@ +export type RawTime = number; +export type AdjustTime = number; +export type FormattedTime = string; + +export type TimerRes = { + raw: number; + adjusted: number; + formatted: string; +}; diff --git a/src/utils/Endpoint.ts b/src/utils/Endpoint.ts deleted file mode 100644 index 8ea5a24..0000000 --- a/src/utils/Endpoint.ts +++ /dev/null @@ -1,51 +0,0 @@ -let endpointHandler: EndpointHandler | null = null; - -export class EndpointHandler { - source: string; - main: string | null; - child: string | null; - - constructor(sourceIn: string) { - this.source = sourceIn; - this.main = null; - this.child = null; - } - - getSource() { - return this.source; - } - - getMain() { - return this.main; - } - - getChild() { - return this.child; - } - - setMain(mainIn: string) { - this.main = mainIn; - } - - setChild(childIn: string) { - this.child = childIn; - } -} - -export function initEndpointHandler(source: string): EndpointHandler { - if (endpointHandler) return endpointHandler; - endpointHandler = new EndpointHandler(source); - return endpointHandler; -} - -export function getEndpointHandler(): EndpointHandler { - if (!endpointHandler) - throw new Error( - "Endpoint Handler not initialized. Call initEndpointHandler(source:string) first." - ); - return endpointHandler; -} - -export function nullifyEndpointHandler() { - endpointHandler = null; -} diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index 7367795..4333910 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -5,17 +5,18 @@ import { queryTimer } from "./Timer"; let logger: Logger | null = null; enum AnsiColor { + ERROR = "\x1b[31m", SUCCESS = "\x1b[32m", - INFO = "\x1b[36m", WARN = "\x1b[33m", - ERROR = "\x1b[31m", + TELEMETRY = "\x1b[34m", DEBUG = "\x1b[35m", + INFO = "\x1b[36m", FORMAT = "\x1b[37m", RESET = "\x1b[0m", } enum FormattingConstant { - MAX_TYPE_LENGTH = 7, // "success".length + MAX_TYPE_LENGTH = 9, // "success".length PROGRESS_BAR_FILLED = "█", PROGRESS_BAR_EMPTY = "░", FORMATTING_DASH = "-", @@ -175,6 +176,7 @@ export class Logger { warn: AnsiColor.WARN, error: AnsiColor.ERROR, debug: AnsiColor.DEBUG, + telemetry: AnsiColor.TELEMETRY, format: AnsiColor.FORMAT, }; diff --git a/src/utils/Timer.ts b/src/utils/Timer.ts index 157a198..5bb90ff 100644 --- a/src/utils/Timer.ts +++ b/src/utils/Timer.ts @@ -1,3 +1,5 @@ +import { TimerRes } from "../types/timer"; + let timers: Map = new Map(); export class Timer { @@ -10,11 +12,7 @@ export class Timer { getTime( unit: "micro" | "ms" | "s" | "m" | "auto" = "auto", precision: number = 2 - ): { - raw: number; - adjusted: number; - formatted: string; - } { + ): TimerRes { const timeTaken_ms = performance.now() - this.startTime; let adjustedTime: number = 0; let unitLabel: string = ""; diff --git a/src/utils/grok/formatter.ts b/src/utils/grok/formatter.ts index c967593..2a3c1b0 100644 --- a/src/utils/grok/formatter.ts +++ b/src/utils/grok/formatter.ts @@ -1,8 +1,19 @@ import { ChatCompletionCreateParamsNonStreaming } from "openai/resources/chat/completions"; -import { GrokController } from "../../controllers/GrokController"; -import { GrokInputData, SystemPromptChoice } from "../../types/grok"; +import { AiInputData } from "../../types/ai"; +import { SystemPrompt } from "../../config/routes"; import { findDcUsernameById } from "../bot/usernames"; import { getDbHandler } from "../../db/DbHandler"; +import { + DEFAULT_PROMPT, + HOGICHAN_PROMPT, + NOMICORD_PROMPT, +} from "../../loaders/storage"; + +const SYSTEM_PROMPTS = { + default: DEFAULT_PROMPT, + hogichan: HOGICHAN_PROMPT, + nomicord: NOMICORD_PROMPT, +} as const; export function formatQuestion( discordUsername: string, @@ -21,22 +32,22 @@ function filter(text: string, discordUsername: string) { } export async function formatCompletion( - reqBody: GrokInputData, + reqBody: AiInputData, model: string, - type: SystemPromptChoice, - endpointName: string + type: SystemPrompt, + modelName: string ): Promise { const completion: ChatCompletionCreateParamsNonStreaming = { model: model, messages: [ { role: "system", - content: GrokController.systemPromptMappings[type], + content: SYSTEM_PROMPTS[type], }, { role: "user", content: formatQuestion( - await getDbHandler().getUsername(reqBody.userId, endpointName), + await getDbHandler().getUsername(reqBody.userId, modelName), reqBody.prompt, reqBody.context ), diff --git a/src/utils/grok/grok.ts b/src/utils/grok/grok.ts index bffad84..7ebcd84 100644 --- a/src/utils/grok/grok.ts +++ b/src/utils/grok/grok.ts @@ -1,6 +1,6 @@ import { getGrokClient } from "../../clients/GrokClient"; import { env } from "../../config/config"; -import { GrokTokenizeReq } from "../../types/grok"; +import { GrokTokenizeReq } from "../../types/ai"; import { getLogger } from "../Logger"; export async function tokenizeGrok( diff --git a/src/utils/parentClasses/ErrorProne.ts b/src/utils/parentClasses/ErrorProne.ts index e685612..9d02276 100644 --- a/src/utils/parentClasses/ErrorProne.ts +++ b/src/utils/parentClasses/ErrorProne.ts @@ -15,19 +15,23 @@ export class ErrorProne { }; } - protected setError(code: number, message: string): void { + protected setError(code: number, message: string): StandardError { this.error.code = code; this.error.status = true; this.error.send = true; this.error.message = message; this.error.time = new Date(); + + return this.error; } - protected setWarn(message: string): void { + protected setWarn(message: string): StandardError { this.error.code = 200; this.error.status = true; this.error.send = true; this.error.message = message; this.error.time = new Date(); + + return this.error; } } diff --git a/src/utils/parsers/OredicBuilder.ts b/src/utils/parsers/OredicBuilder.ts new file mode 100644 index 0000000..8c56846 --- /dev/null +++ b/src/utils/parsers/OredicBuilder.ts @@ -0,0 +1,161 @@ +import { set } from "zod"; +import { OredicPack } from "../../config/routes"; +import { StandardError } from "../../types/errors"; +import { + Ast, + BuildOptions, + Matches, + NodeNames, + OperatorChar, + OrNode, + PatternNode, +} from "../../types/parsing"; +import { ErrorProne } from "../parentClasses/ErrorProne"; +import { OredicParser } from "./OredicParser"; +import { OredicShortener } from "./OredicShortener"; +export class OredicBuilder extends ErrorProne { + Shortener: OredicShortener; + Parser: OredicParser; + shortcuts: Map; + private buildPipeline: Array<(matches: Matches) => Ast | StandardError>; + + constructor( + private pack: OredicPack, + private options: BuildOptions + ) { + super(); + + this.Shortener = new OredicShortener("nomi-ceu"); + this.Parser = new OredicParser(); + this.shortcuts = this.Shortener.getShortcuts(); + + this.buildPipeline = []; + + if (options.shortenedDisjunction) { + this.buildPipeline.push(this.shortenedDisjuntion.bind(this)); + } + } + + buildFromMatches(matches: Matches): Ast[] | StandardError { + const candidates = new Set(); + + for (const buildStrategy of this.buildPipeline) { + const result = buildStrategy(matches); + + if (result && typeof result === "object" && result.type === "error") { + return result; + } + if (result) { + candidates.add(result); + } + } + + if (candidates.size === 0) { + return this.setError(500, "No valid AST candidates generated"); + } + return Array.from(candidates); + } + + buildFromAsts(asts: Ast[]) { + const candidates = new Set(); + + for (const ast of asts) { + const built = this.buildFromAst(ast); + + if (!built) { + continue; + } + if (typeof built !== "string") return built; + + candidates.add(built); + } + + return Array.from(candidates).sort((a, b) => a.length - b.length)[0]; + } + + private shortenedDisjuntion(matches: Matches): OrNode | StandardError { + const children: PatternNode[] = []; + for (const match in matches) { + const candidate = this.shortcuts.get(match); + const rawPattern = candidate ? candidate : match; + const nodeCandidate = this.Parser.parse(rawPattern); + + if (!nodeCandidate) { + continue; + } + if (nodeCandidate.type === "error") { + return nodeCandidate; + } + if (nodeCandidate.type === NodeNames.OPERATOR) { + return this.setError(500, "Parsed a pattern as an operator"); + } + + children.push(nodeCandidate); + } + + return { + type: NodeNames.OPERATOR, + operator: "OR", + negation: false, + children: children, + }; + } + + private buildFromAst(ast: Ast): string | StandardError { + if (!ast) { + return ""; + } + + switch (ast.type) { + case NodeNames.OPERATOR: + const char: OperatorChar = this.getOperatorChar(ast.operator); + const children: string[] = []; + + for (let i = 0; i < ast.children.length; i++) { + const child: Ast = ast.children[i]; + let subFilter = this.buildFromAst(child); + + if (typeof subFilter !== "string") return subFilter; + + if (child.type === NodeNames.OPERATOR) { + const needsParens = i > 0 && child.operator !== ast.operator; + if (needsParens) { + subFilter = `(${subFilter})`; + } + } + + if (child.negation) { + subFilter = `!${subFilter}`; + } + + children.push(subFilter); + } + return children.join(char); + + case NodeNames.PATTERN: + return ast.children + .map((token) => { + if (token.type === NodeNames.WILDCARD) { + return "*"; + } else if (token.type === NodeNames.TEXT) { + return token.content; + } + return ""; + }) + .join(""); + } + } + + private getOperatorChar(string: "AND" | "OR" | "XOR"): OperatorChar { + switch (string) { + case "AND": + return OperatorChar.AND; + case "OR": + return OperatorChar.OR; + case "XOR": + return OperatorChar.XOR; + default: + throw new Error(`Invalid operator string: ${string}`); + } + } +} diff --git a/src/utils/parsers/OredicMatcher.ts b/src/utils/parsers/OredicMatcher.ts index c1a3606..13d7e9d 100644 --- a/src/utils/parsers/OredicMatcher.ts +++ b/src/utils/parsers/OredicMatcher.ts @@ -1,7 +1,7 @@ +import { OredicPack } from "../../config/routes"; import { DUMPS } from "../../loaders/storage"; import { AstNode, - SupportedPack, exAst, RegexNode, OredicNode, @@ -10,6 +10,8 @@ import { exXorNode, PatternNode, NodeNames, + exAstNode, + Matches, } from "../../types/parsing"; import { ErrorProne } from "../parentClasses/ErrorProne"; import { OredicParser } from "./OredicParser"; @@ -21,41 +23,66 @@ const EMPTY_OREDIC_NODE: OredicNode = { } as const; export class OredicMatcher extends ErrorProne { - private validOredics: string[] | null; - private ast: exAst; private parser: OredicParser; + private dump: string[]; - constructor( - rules: AstNode | PatternNode | RegexNode | OredicNode | string, - private pack: SupportedPack - ) { + constructor(private pack: OredicPack) { super(); - this.pack = "nomi-ceu"; this.parser = new OredicParser(); - this.ast = this.parseInputRules(rules); - this.validOredics = null; + const dump = DUMPS.get(this.pack); + this.dump = dump ? dump.split("\n") : []; + } + + match(rules: exAstNode): Matches | null { + return this.parse(rules); } - private parseInputRules( - rules: AstNode | RegexNode | OredicNode | string - ): exAst { - if (typeof rules === "string") { - const parseResult = this.parser.parse(rules); - const isErrorResult = - !parseResult || (parseResult as any).type === "error"; - return isErrorResult ? null : (parseResult as AstNode); + isUniqueMatch(pattern: string, targetIndex: number): boolean { + if (targetIndex < 0 || targetIndex >= this.dump.length) { + return false; } - return rules; - } + const target = this.dump[targetIndex]; + const regex = this.buildRegexFromPattern(pattern); + + if (!regex.test(target)) { + return false; + } + + const maxRadius = 128; + for (let radius = 1; radius <= maxRadius; radius *= 2) { + for (let offset = -radius; offset <= radius; offset++) { + const checkIndex = targetIndex + offset; + + if ( + checkIndex === targetIndex || + checkIndex < 0 || + checkIndex >= this.dump.length + ) { + continue; + } + + regex.lastIndex = 0; + if (regex.test(this.dump[checkIndex])) { + return false; + } + } + } + + for (let i = 0; i < this.dump.length; i++) { + const distance = Math.abs(i - targetIndex); + + if (distance <= maxRadius || i === targetIndex) { + continue; + } - match(): string[] | null { - if (this.validOredics !== null) { - return this.validOredics; + regex.lastIndex = 0; + if (regex.test(this.dump[i])) { + return false; + } } - this.validOredics = this.parse(this.ast); - return this.validOredics; + return true; } /* @@ -302,6 +329,19 @@ export class OredicMatcher extends ErrorProne { return this.newOredicNode(newChildren); } + private buildRegexFromPattern(pattern: string): RegExp { + let regexPattern = "^"; + for (let i = 0; i < pattern.length; i++) { + if (pattern[i] === "*") { + regexPattern += ".+"; + } else { + regexPattern += pattern[i]; + } + } + regexPattern += "$"; + return new RegExp(regexPattern); + } + private newOredicNode(children: string[] | null): OredicNode { return { type: NodeNames.OREDIC, diff --git a/src/utils/parsers/OredicShortener.ts b/src/utils/parsers/OredicShortener.ts index bc5ee7c..206f752 100644 --- a/src/utils/parsers/OredicShortener.ts +++ b/src/utils/parsers/OredicShortener.ts @@ -1,254 +1,185 @@ import { DUMPS } from "../../loaders/storage"; -import { Ast, exAst, NodeNames, SupportedPack } from "../../types/parsing"; +import { Ast, NodeNames } from "../../types/parsing"; +import { TimerRes } from "../../types/timer"; import { getLogger } from "../Logger"; import { ErrorProne } from "../parentClasses/ErrorProne"; import { OredicMatcher } from "./OredicMatcher"; -import { startTimer } from "../Timer"; +import { startTimer, stopTimer, queryTimer, Timer } from "../Timer"; +import { OredicPack } from "../../config/routes"; const PROGRESS_UPDATE_INTERVAL = 10; -export class OredicSubstrings extends ErrorProne { +type TelemetryResults = { + result: { + string: string; + length: number; + shortening: number; + }; + timings: TimerRes; +}; + +export class OredicShortener extends ErrorProne { private dump: string[]; - private shortestPatterns: Map; + private matcher: OredicMatcher; + private telemetry: Map; + private shortcuts: Map; - constructor(private pack: SupportedPack) { + constructor(private pack: OredicPack) { super(); this.dump = this.loadDump(); - this.shortestPatterns = new Map(); - this.findShortestPatterns(); + this.matcher = new OredicMatcher(this.pack); + this.telemetry = new Map(); + this.shortcuts = new Map(); } - /* - * Main processing - */ - simplifyPatterns(ast: Ast) { - if (!ast) return null; - - if (ast.type === NodeNames.PATTERN) { - for (const child of ast.children) { - if (child.type === NodeNames.WILDCARD) continue; - - const newText = this.shortestPatterns.get(child.content); - - if (!newText) continue; + getShortcuts() { + //TODO: Implement caching strategy!!! - delete (child as any).content; - child.content = newText; - } - return ast; + if (!this.shortcuts) { + this.shortcuts = this.findAll(); } - for (const child of ast.children) { - this.simplifyPatterns(child); + if (!this.shortcuts) { + throw new Error("Shortcuts didn't generate"); } - } - private findShortestPatterns(): void { - getLogger().simpleLog( - "info", - `Finding shortest unique patterns for ${this.pack}` - ); - startTimer("substrings"); - - for (let position = 0; position < this.dump.length; position++) { - const oredicName = this.dump[position]; - const shortestPattern = this.findShortestUniquePattern(oredicName); - this.shortestPatterns.set(oredicName, shortestPattern); + return this.shortcuts; + } - const currentIndex = position + 1; - const shouldUpdateProgress = - currentIndex % PROGRESS_UPDATE_INTERVAL === 0 || - currentIndex === this.dump.length; + getTelemetry(): Map { + return new Map(this.telemetry); + } - if (shouldUpdateProgress) { - this.updateProgress(currentIndex, this.dump.length); + private findAll(): Map { + getLogger().simpleLog("info", `Finding shortest patterns for ${this.pack}`); + startTimer("shortening"); + + const result = new Map(); + + for (let i = 0; i < this.dump.length; i++) { + const oredicTimer = new Timer(); + const oredic = this.dump[i]; + const pattern = this.findShortest(oredic, i); + result.set(oredic, pattern); + + const timerResult = oredicTimer.getTime(); + this.telemetry.set(oredic, { + timings: timerResult, + result: { + string: pattern, + length: pattern.length, + shortening: oredic.length - pattern.length, + }, + }); + + if ( + (i + 1) % PROGRESS_UPDATE_INTERVAL === 0 || + i === this.dump.length - 1 + ) { + this.logProgress(i + 1, this.dump.length); } } + const time = stopTimer("shortening").getTime(); getLogger().simpleLog( "success", - `Found shortest patterns for ${this.dump.length} oredics` + `Found ${result.size} patterns in ${time.formatted}` ); - } - private findShortestUniquePattern(targetOredic: string): string { - const patternGenerators = [ - () => this.generateSingleCharPatterns(targetOredic), - () => this.generateTwoCharPatterns(targetOredic), - () => this.generateThreeCharPatterns(targetOredic), - () => this.generatePrefixSuffixPatterns(targetOredic), - () => [targetOredic], - ]; - - for (const generator of patternGenerators) { - const candidatePatterns = generator(); - - for (const pattern of candidatePatterns) { - if (this.isUniqueMatch(pattern, targetOredic)) { - return pattern; - } - } - } - - return targetOredic; - } - - /* - * Pattern validation - */ - - private isUniqueMatch(pattern: string, targetOredic: string): boolean { - const matcher = new OredicMatcher(pattern, this.pack); - const matchingOredics = matcher.match(); - - const hasExactlyOneMatch = matchingOredics?.length === 1; - const matchesTarget = matchingOredics?.[0] === targetOredic; - - return hasExactlyOneMatch && matchesTarget; + return result; } - /* - * Pattern generation - */ - - private generateSingleCharPatterns(text: string): string[] { - const patterns: string[] = []; - const lastIndex = text.length - 1; + private findShortest(oredic: string, oredicIndex: number): string { + const maxUsefulLength = oredic.length - 2; - for (let index = 0; index < text.length; index++) { - const char = text[index]; - const isFirstChar = index === 0; - const isLastChar = index === lastIndex; - - patterns.push(`*${char}*`); - - if (isFirstChar) { - patterns.push(`${char}*`); - } - - if (isLastChar) { - patterns.push(`*${char}`); - } + for (let length = 1; length <= maxUsefulLength; length++) { + const pattern = this.tryLength(oredic, oredicIndex, length); + if (pattern) return pattern; } - return patterns; + return oredic; } - private generateTwoCharPatterns(text: string): string[] { - const patterns: string[] = []; - - this.generateConsecutivePairs(text, patterns); - this.generateNonConsecutivePairs(text, patterns); - - return patterns; + private tryLength( + text: string, + oredicIndex: number, + length: number + ): string | null { + return this.tryPositions(text, oredicIndex, length, 0, []); } - private generateConsecutivePairs(text: string, patterns: string[]): void { - const lastPairIndex = text.length - 2; - - for (let index = 0; index < text.length - 1; index++) { - const pair = text.substring(index, index + 2); - const isFirstPair = index === 0; - const isLastPair = index === lastPairIndex; - - patterns.push(`*${pair}*`); - - if (isFirstPair) { - patterns.push(`${pair}*`); + private tryPositions( + text: string, + oredicIndex: number, + remaining: number, + start: number, + positions: number[] + ): string | null { + if (remaining === 0) { + if (this.hasUselessGaps(positions)) { + return null; } - if (isLastPair) { - patterns.push(`*${pair}`); - } + const pattern = this.buildPattern(text, positions); + return this.matcher.isUniqueMatch(pattern, oredicIndex) ? pattern : null; } - } - private generateNonConsecutivePairs(text: string, patterns: string[]): void { - const lastIndex = text.length - 1; - - for (let firstIndex = 0; firstIndex < text.length - 1; firstIndex++) { - const firstChar = text[firstIndex]; - - for ( - let secondIndex = firstIndex + 2; - secondIndex < text.length; - secondIndex++ - ) { - const secondChar = text[secondIndex]; - const isAtBoundary = firstIndex > 0 || secondIndex < lastIndex; + const maxPos = text.length - remaining; + for (let pos = start; pos <= maxPos; pos++) { + positions.push(pos); + const result = this.tryPositions( + text, + oredicIndex, + remaining - 1, + pos + 1, + positions + ); + if (result) return result; + positions.pop(); + } - patterns.push(`${firstChar}*${secondChar}`); + return null; + } - if (isAtBoundary) { - patterns.push(`*${firstChar}*${secondChar}*`); - } + private hasUselessGaps(positions: number[]): boolean { + for (let i = 0; i < positions.length - 1; i++) { + const gapSize = positions[i + 1] - positions[i] - 1; + if (gapSize === 1) { + return true; } } + return false; } - private generateThreeCharPatterns(text: string): string[] { - const patterns: string[] = []; - const lastTripletIndex = text.length - 3; + private buildPattern(text: string, positions: number[]): string { + if (positions.length === 0) return ""; - for (let index = 0; index < text.length - 2; index++) { - const triplet = text.substring(index, index + 3); - const isFirstTriplet = index === 0; - const isLastTriplet = index === lastTripletIndex; + const parts: string[] = []; - patterns.push(`*${triplet}*`); + if (positions[0] > 0) parts.push("*"); - if (isFirstTriplet) { - patterns.push(`${triplet}*`); - } + for (let i = 0; i < positions.length; i++) { + parts.push(text[positions[i]]); - if (isLastTriplet) { - patterns.push(`*${triplet}`); + if (i < positions.length - 1 && positions[i + 1] > positions[i] + 1) { + parts.push("*"); } } - return patterns; - } - - private generatePrefixSuffixPatterns(text: string): string[] { - const patterns: string[] = []; - - for (let length = 4; length < text.length; length++) { - const prefix = text.substring(0, length); - const suffixStartIndex = text.length - length; - const suffix = text.substring(suffixStartIndex); + if (positions[positions.length - 1] < text.length - 1) parts.push("*"); - patterns.push(`${prefix}*`); - patterns.push(`*${suffix}`); - } - - return patterns; + return parts.join(""); } - /* - * Utility methods - */ - private loadDump(): string[] { const dumpContent = DUMPS.get(this.pack); - if (!dumpContent) { throw new Error(`Failed to load oredic dump for pack: ${this.pack}`); } - return dumpContent.split("\n"); } - private updateProgress(currentCount: number, totalCount: number): void { - const PROGRESS_BAR_WIDTH = 50; - const TIMER_ID = "substrings"; - const PROGRESS_LABEL = "Processing oredics"; - - getLogger().progressBar( - currentCount, - totalCount, - TIMER_ID, - PROGRESS_BAR_WIDTH, - PROGRESS_LABEL - ); + private logProgress(current: number, total: number): void { + getLogger().progressBar(current, total, "shortening", 50, "Processing"); } } diff --git a/storage/oredic/dumps/nomi-ceu.txt b/storage/oredic/dumps/nomi-ceu.txt index ff0269b..9e502e3 100644 --- a/storage/oredic/dumps/nomi-ceu.txt +++ b/storage/oredic/dumps/nomi-ceu.txt @@ -7441,4 +7441,3 @@ orePoorLead orePoorSilver orePoorNickel orePoorZinc - From fd5f83b8d13d7c739153d763180081494e536db4 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Tue, 18 Nov 2025 03:41:54 +0100 Subject: [PATCH 17/18] everything but caching and better Redis Usage --- .gitignore | 2 + main.mjs | 3 - src/controllers/OredicController.ts | 56 +- src/services/oredic/OredicService.ts | 123 +- src/types/errors.ts | 2 + src/types/express.ts | 31 +- src/types/parsing.ts | 73 +- src/types/server.ts | 22 - src/utils/parentClasses/ErrorProne.ts | 71 +- src/utils/parsers/OredicBuilder.ts | 150 +- src/utils/parsers/OredicMatcher.ts | 57 +- src/utils/parsers/OredicParser.ts | 59 +- src/utils/parsers/OredicShortener.ts | 19 +- storage/oredic/dumps/.gitkeep | 0 storage/oredic/dumps/nomi-ceu.txt | 7443 ------------------------- 15 files changed, 441 insertions(+), 7670 deletions(-) delete mode 100644 main.mjs create mode 100644 storage/oredic/dumps/.gitkeep delete mode 100644 storage/oredic/dumps/nomi-ceu.txt diff --git a/.gitignore b/.gitignore index 7d23d01..bc1fbea 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ ecosystem.config.js dist/ src/local/ + +storage/oredic/dumps/*.txt diff --git a/main.mjs b/main.mjs deleted file mode 100644 index 6ca5d30..0000000 --- a/main.mjs +++ /dev/null @@ -1,3 +0,0 @@ -console.warn( - "main.mjs is deprecated. The server entrypoint now lives in index.ts." -); diff --git a/src/controllers/OredicController.ts b/src/controllers/OredicController.ts index 1cee79a..92c2191 100644 --- a/src/controllers/OredicController.ts +++ b/src/controllers/OredicController.ts @@ -1,22 +1,54 @@ import { Request, Response, NextFunction } from "express"; import { OredicService } from "../services/oredic/OredicService"; +import { assertOredicRequest, OredicRequest } from "../types/express"; +import { OredicPack } from "../config/routes"; +import { ErrorProne } from "../utils/parentClasses/ErrorProne"; -export class OredicController { - private service = new OredicService(); +export class OredicController extends ErrorProne { + constructor() { + super(); + } - simplify = async (req: Request, res: Response, next: NextFunction) => { - const { pack, action } = req.oredic || {}; + parse = async (req: Request, res: Response, next: NextFunction) => { + assertOredicRequest(req); + const service = this.initializeService(req.oredic.pack); + const filter = req.body.filter; - if (!pack || !action) { - return next(new Error("Pack or action is missing")); + if (!filter) { + this.setError( + 500, + "Received no filter in OredicController. Should have been validated properly first." + ); + res.status(this.getErrorCode()).send(this.sendErrorToClient()); + return; } - try { - const result = await this.service.simplify(req.body.string, pack); - res.data = { ast: result.data }; - next(); - } catch (error) { - next(error); + const parsed = await service.parse(filter); + if (this.isError(parsed)) { + this.propagateError(parsed, "Could not parse", "OredicController"); + res.status(this.getErrorCode()).send(this.sendErrorToClient()); + return; } + + res.data = { ast: parsed }; + }; + + build = async (req: Request, res: Response, next: NextFunction) => { + assertOredicRequest(req); + const service = this.initializeService(req.oredic.pack); + }; + + match = async (req: Request, res: Response, next: NextFunction) => { + assertOredicRequest(req); + const service = this.initializeService(req.oredic.pack); }; + + simplify = async (req: Request, res: Response, next: NextFunction) => { + assertOredicRequest(req); + const service = this.initializeService(req.oredic.pack); + }; + + initializeService(pack: OredicPack) { + return new OredicService(pack); + } } diff --git a/src/services/oredic/OredicService.ts b/src/services/oredic/OredicService.ts index f37ebdb..de25e32 100644 --- a/src/services/oredic/OredicService.ts +++ b/src/services/oredic/OredicService.ts @@ -1,58 +1,103 @@ import { OredicParser } from "../../utils/parsers/OredicParser"; import { OredicMatcher } from "../../utils/parsers/OredicMatcher"; import { OredicPack } from "../../config/routes"; -import { Ast } from "../../types/parsing"; +import { AstNode, BuildOptions, OredicMatches } from "../../types/parsing"; import { StandardError } from "../../types/errors"; +import { ErrorProne } from "../../utils/parentClasses/ErrorProne"; +import { OredicBuilder } from "../../utils/parsers/OredicBuilder"; -type OredicResult = { - type: "success" | "error"; - data?: any; - error?: string; -}; - -export class OredicService { - private parser = new OredicParser(); +export class OredicService extends ErrorProne { + constructor(private pack: OredicPack) { + super("OredicService"); + } - async simplify(input: string, pack: OredicPack): Promise { - const parseResult = this.parse(input); + private parser = new OredicParser(this.pack); + private matcher = new OredicMatcher(this.pack); + private builder = new OredicBuilder(this.pack); + async parse(input: string): Promise { + const parseResult = this.parser.parse(input); if (!parseResult) { - return { - type: "error", - error: "Failed to parse input", - }; + return this.setError( + 500, + "Unknown Error: Could not parse input", + "parse" + ); } + return parseResult; + } + + async match(ast: AstNode): Promise { + const matchResult = this.matcher.match(ast); + return matchResult; + } - if ((parseResult as StandardError).type === "error") { - const error = parseResult as StandardError; - return { - type: "error", - error: error.message || "Parse error", - }; + async buildAsts( + matches: OredicMatches, + options: BuildOptions + ): Promise { + const builtAst = this.builder.buildFromMatches(matches, options); + return builtAst; + } + + async buildSingleAst( + matches: OredicMatches, + options: BuildOptions + ): Promise { + let optionsCount = 0; + + for (const option in options) { + if (options[option as keyof BuildOptions] === true) { + optionsCount += 1; + } } - try { - const ast = parseResult as Ast; - const result = this.match(ast, pack); - return { - type: "success", - data: result, - }; - } catch (error) { - return { - type: "error", - error: error instanceof Error ? error.message : "Unknown error", - }; + if (optionsCount !== 1) { + return this.setError( + 400, + "Must select one and only one way to build the ast.", + "buildSingleAst" + ); } + + const builtAst = this.builder.buildFromMatches(matches, options); + + if (this.isError(builtAst)) { + return this.propagateError( + builtAst, + "Failed to build AST", + "buildSingleAst" + ); + } + + if (builtAst.length === 0) { + return this.setError(400, "No AST was built", "buildSingleAst"); + } + + return builtAst[0]; } - private parse(input: string): Ast | StandardError { - return this.parser.parse(input); + async build(ast: AstNode): Promise { + return this.builder.buildFromAst(ast); } - private match(ast: Ast, pack: OredicPack) { - if (!ast) throw new Error("AST is null"); - const matcher = new OredicMatcher(pack); - return matcher.match(ast); + async buildAndFindBest(asts: AstNode[]): Promise { + let candidates: string[] = []; + + for (const ast of asts) { + const candidate = this.builder.buildFromAst(ast); + + if (this.isError(candidate)) { + return this.propagateError( + candidate, + "Failed to find best string from Ast" + ); + } + + candidates.push(candidate); + } + + candidates = candidates.sort((a, b) => a.length - b.length); + return candidates[0]; } } diff --git a/src/types/errors.ts b/src/types/errors.ts index 64bf4e2..f463f2d 100644 --- a/src/types/errors.ts +++ b/src/types/errors.ts @@ -8,4 +8,6 @@ export type StandardError = { message: string | null; location: PathLike | null; time: Date | null; + context?: string | null; // Method/class context where error occurred + stackTrace?: StandardError[]; // Chain of errors from deepest to shallowest }; diff --git a/src/types/express.ts b/src/types/express.ts index fbbe21c..612ca6e 100644 --- a/src/types/express.ts +++ b/src/types/express.ts @@ -6,7 +6,8 @@ import { } from "../config/routes"; import { TimerRes } from "./timer"; import { ChatCompletion } from "openai/resources/index"; -import { exAst } from "./parsing"; +import { AstNode, OredicMatches } from "./parsing"; +import { Request } from "express"; declare global { namespace Express { @@ -44,11 +45,35 @@ export type MembersData = { usernames?: string[]; }; export type OredicData = { - ast?: exAst; - matches?: string[]; + ast?: AstNode; + matches?: OredicMatches; bestFilter?: { + ast: AstNode; string: string; }; }; +// Request type helpers for controllers +export type OredicRequestBody = { + filter?: string; + ast?: AstNode; + matches?: string[]; +}; + +export type OredicRequest = Request<{}, any, OredicRequestBody> & { + oredic: { + pack: OredicPack; + action: OredicAction; + }; +}; + +export function assertOredicRequest( + req: Request +): asserts req is OredicRequest { + const r = req as any; + if (!r.oredic?.pack || !r.oredic?.action) { + throw new Error("Invalid request: missing oredic context"); + } +} + export {}; diff --git a/src/types/parsing.ts b/src/types/parsing.ts index ad9bb04..c2a12ba 100644 --- a/src/types/parsing.ts +++ b/src/types/parsing.ts @@ -29,25 +29,54 @@ export enum NodeNames { WILDCARD = "wildcard", } -export type ParseState = { - ast: Ast; - tokenBuffer: Token[]; - operandBuffer: AstNode[]; - currentOperator: "AND" | "XOR" | "OR" | null; - negationFlag: boolean; +export type AstNode = PatternNode | OperatorNode; +export type MatcherNode = + | RegexNode + | OredicNode + | MatcherOperatorNode + | AstNode; + +export type OperatorNode = AndNode | OrNode | XorNode; +export type MatcherOperatorNode = + | MatcherAndNode + | MatcherOrNode + | MatcherXorNode; + +export type LexemeElement = + | TextLexeme + | OperatorLexeme + | NegationLexeme + | WildcardLexeme + | GroupLexeme; + +export type TextLexeme = { + type: LexemeNames.TEXT; + content: string; }; -export type Ast = AstNode | null; -export type exAst = exAstNode | null; +export type OperatorLexeme = { + type: LexemeNames.OPERATOR; + content: "AND" | "OR" | "XOR"; +}; -export type AstNode = PatternNode | OperatorNode; -export type exAstNode = RegexNode | OredicNode | exOperatorNode | AstNode; +export type NegationLexeme = { + type: LexemeNames.NEGATION; + content: null; +}; -export type OperatorNode = AndNode | OrNode | XorNode; +export type WildcardLexeme = { + type: LexemeNames.WILDCARD; + content: null; +}; -export type LexemeElement = { - type: LexemeNames; - content: LexemeElement[] | string | null; +export type GroupLexeme = { + type: LexemeNames.GROUP; + content: LexemeElement[]; +}; + +export type LexicalParseResult = { + lexemes: LexemeElement[]; + consumed: number; }; export type AndNode = { @@ -69,25 +98,23 @@ export type XorNode = { children: Array; }; -export type exOperatorNode = exAndNode | exOrNode | exXorNode; - -export type exAndNode = { +export type MatcherAndNode = { type: NodeNames.OPERATOR; operator: "AND"; negation: boolean; - children: Array; + children: Array; }; -export type exOrNode = { +export type MatcherOrNode = { type: NodeNames.OPERATOR; operator: "OR"; negation: boolean; - children: Array; + children: Array; }; -export type exXorNode = { +export type MatcherXorNode = { type: NodeNames.OPERATOR; operator: "XOR"; negation: boolean; - children: Array; + children: Array; }; export type PatternNode = { @@ -113,7 +140,7 @@ export type Token = | { type: NodeNames.TEXT; content: string } | { type: NodeNames.WILDCARD }; -export type Matches = string[]; +export type OredicMatches = string[]; export type BuildOptions = { shortenedDisjunction: boolean; diff --git a/src/types/server.ts b/src/types/server.ts index 2132215..41ed0da 100644 --- a/src/types/server.ts +++ b/src/types/server.ts @@ -20,25 +20,3 @@ export type PatternConfig = { severity: "critical" | "high" | "medium" | "low"; description: string; }; - -export type EndpointConfig = { - // /source/master/child - // /leveret/grok/nomicord - source: string; - main: string | null; - child: string | null; - config?: RequestEndpointConfig; -}; - -export type RequestEndpointConfig = { - endpointName: string; - systemPrompt: SystemPrompt; - model: string; - maxPromptTokens: number; - maxContextTokens: number; - maxTotalTokens: number; - filters: string; - rateLimit: ConfigEndpointConfig["RATE_LIMIT"]; -}; - -export type EndpointLevel = "source" | "main" | "child"; diff --git a/src/utils/parentClasses/ErrorProne.ts b/src/utils/parentClasses/ErrorProne.ts index 9d02276..a0ffe03 100644 --- a/src/utils/parentClasses/ErrorProne.ts +++ b/src/utils/parentClasses/ErrorProne.ts @@ -1,9 +1,11 @@ import { StandardError } from "../../types/errors"; export class ErrorProne { - error: StandardError; + protected error: StandardError; + protected className: string; - constructor() { + constructor(className?: string) { + this.className = className || this.constructor.name; this.error = { type: "error", code: null, @@ -12,26 +14,87 @@ export class ErrorProne { message: null, location: __dirname, time: null, + context: null, + stackTrace: [], }; } - protected setError(code: number, message: string): StandardError { + protected sendErrorToClient() { + return { + message: this.error.message ? this.error.message : "no message", + culprit: `Error in file: ${this.error.location}, from method: ${this.error.context ? this.error.context : "Uknown"}`, + time: this.error.time ? this.error.time : "Uknown", + stackTrace: this.error.stackTrace, + }; + } + + protected setError( + code: number, + message: string, + methodName?: string + ): StandardError { this.error.code = code; this.error.status = true; this.error.send = true; this.error.message = message; this.error.time = new Date(); + this.error.context = methodName + ? `${this.className}.${methodName}` + : this.className; + this.error.stackTrace = []; return this.error; } - protected setWarn(message: string): StandardError { + protected setWarn(message: string, methodName?: string): StandardError { this.error.code = 200; this.error.status = true; this.error.send = true; this.error.message = message; this.error.time = new Date(); + this.error.context = methodName + ? `${this.className}.${methodName}` + : this.className; + this.error.stackTrace = []; return this.error; } + + protected propagateError( + childError: StandardError, + message: string, + methodName?: string + ): StandardError { + const context = methodName + ? `${this.className}.${methodName}` + : this.className; + + return { + type: "error", + code: childError.code, + status: true, + send: true, + message: message, + location: __dirname, + time: new Date(), + context: context, + stackTrace: [childError, ...(childError.stackTrace || [])], + }; + } + + protected getErrorCode() { + const errorCode = this.error.code; + if (!errorCode) { + this.propagateError( + this.error, + "Could not even get error code... somehow? Defaulting to 500. If you see this, the API is fucked." + ); + return 500; + } + return errorCode; + } + + protected isError(value: any): value is StandardError { + return value && typeof value === "object" && value.type === "error"; + } } diff --git a/src/utils/parsers/OredicBuilder.ts b/src/utils/parsers/OredicBuilder.ts index 8c56846..02050ff 100644 --- a/src/utils/parsers/OredicBuilder.ts +++ b/src/utils/parsers/OredicBuilder.ts @@ -2,9 +2,9 @@ import { set } from "zod"; import { OredicPack } from "../../config/routes"; import { StandardError } from "../../types/errors"; import { - Ast, + AstNode, BuildOptions, - Matches, + OredicMatches, NodeNames, OperatorChar, OrNode, @@ -14,36 +14,39 @@ import { ErrorProne } from "../parentClasses/ErrorProne"; import { OredicParser } from "./OredicParser"; import { OredicShortener } from "./OredicShortener"; export class OredicBuilder extends ErrorProne { - Shortener: OredicShortener; - Parser: OredicParser; - shortcuts: Map; - private buildPipeline: Array<(matches: Matches) => Ast | StandardError>; + private shortener: OredicShortener; + private parser: OredicParser; + private shortcuts: Map; - constructor( - private pack: OredicPack, - private options: BuildOptions - ) { - super(); + constructor(private pack: OredicPack) { + super("OredicBuilder"); - this.Shortener = new OredicShortener("nomi-ceu"); - this.Parser = new OredicParser(); - this.shortcuts = this.Shortener.getShortcuts(); + this.shortener = new OredicShortener(pack); + this.parser = new OredicParser(pack); - this.buildPipeline = []; - - if (options.shortenedDisjunction) { - this.buildPipeline.push(this.shortenedDisjuntion.bind(this)); + const shortcuts = this.shortener.getShortcuts(); + if (this.isError(shortcuts)) { + throw new Error(`Failed to load shortcuts: ${shortcuts.message}`); } + this.shortcuts = shortcuts; } - buildFromMatches(matches: Matches): Ast[] | StandardError { - const candidates = new Set(); + buildFromMatches( + matches: OredicMatches, + options: BuildOptions + ): AstNode[] | StandardError { + const candidates = new Set(); + const pipeline = this.buildPipeline(options); - for (const buildStrategy of this.buildPipeline) { + for (const buildStrategy of pipeline) { const result = buildStrategy(matches); - if (result && typeof result === "object" && result.type === "error") { - return result; + if (this.isError(result)) { + return this.propagateError( + result, + "Build strategy failed", + "buildFromMatches" + ); } if (result) { candidates.add(result); @@ -51,57 +54,16 @@ export class OredicBuilder extends ErrorProne { } if (candidates.size === 0) { - return this.setError(500, "No valid AST candidates generated"); + return this.setError( + 500, + "No valid AST candidates generated", + "buildFromMatches" + ); } return Array.from(candidates); } - buildFromAsts(asts: Ast[]) { - const candidates = new Set(); - - for (const ast of asts) { - const built = this.buildFromAst(ast); - - if (!built) { - continue; - } - if (typeof built !== "string") return built; - - candidates.add(built); - } - - return Array.from(candidates).sort((a, b) => a.length - b.length)[0]; - } - - private shortenedDisjuntion(matches: Matches): OrNode | StandardError { - const children: PatternNode[] = []; - for (const match in matches) { - const candidate = this.shortcuts.get(match); - const rawPattern = candidate ? candidate : match; - const nodeCandidate = this.Parser.parse(rawPattern); - - if (!nodeCandidate) { - continue; - } - if (nodeCandidate.type === "error") { - return nodeCandidate; - } - if (nodeCandidate.type === NodeNames.OPERATOR) { - return this.setError(500, "Parsed a pattern as an operator"); - } - - children.push(nodeCandidate); - } - - return { - type: NodeNames.OPERATOR, - operator: "OR", - negation: false, - children: children, - }; - } - - private buildFromAst(ast: Ast): string | StandardError { + buildFromAst(ast: AstNode): string | StandardError { if (!ast) { return ""; } @@ -112,7 +74,7 @@ export class OredicBuilder extends ErrorProne { const children: string[] = []; for (let i = 0; i < ast.children.length; i++) { - const child: Ast = ast.children[i]; + const child: AstNode = ast.children[i]; let subFilter = this.buildFromAst(child); if (typeof subFilter !== "string") return subFilter; @@ -146,6 +108,52 @@ export class OredicBuilder extends ErrorProne { } } + private shortenedDisjuntion(matches: OredicMatches): OrNode | StandardError { + const children: PatternNode[] = []; + for (const match in matches) { + const candidate = this.shortcuts.get(match); + const rawPattern = candidate ? candidate : match; + const nodeCandidate = this.parser.parse(rawPattern); + + if (!nodeCandidate) { + continue; + } + if (this.isError(nodeCandidate)) { + return this.propagateError( + nodeCandidate, + "Failed to parse pattern", + "shortenedDisjuntion" + ); + } + if (nodeCandidate.type === NodeNames.OPERATOR) { + return this.setError( + 500, + "Parsed a pattern as an operator", + "shortenedDisjuntion" + ); + } + + children.push(nodeCandidate); + } + + return { + type: NodeNames.OPERATOR, + operator: "OR", + negation: false, + children: children, + }; + } + + private buildPipeline(options: BuildOptions) { + const pipeline = []; + + if (options.shortenedDisjunction) { + pipeline.push(this.shortenedDisjuntion.bind(this)); + } + + return pipeline; + } + private getOperatorChar(string: "AND" | "OR" | "XOR"): OperatorChar { switch (string) { case "AND": diff --git a/src/utils/parsers/OredicMatcher.ts b/src/utils/parsers/OredicMatcher.ts index 13d7e9d..d6f9b98 100644 --- a/src/utils/parsers/OredicMatcher.ts +++ b/src/utils/parsers/OredicMatcher.ts @@ -1,17 +1,17 @@ import { OredicPack } from "../../config/routes"; import { DUMPS } from "../../loaders/storage"; +import { StandardError } from "../../types/errors"; import { AstNode, - exAst, RegexNode, OredicNode, - exAndNode, - exOrNode, - exXorNode, PatternNode, NodeNames, - exAstNode, - Matches, + MatcherNode, + MatcherAndNode, + MatcherOrNode, + MatcherXorNode, + OredicMatches, } from "../../types/parsing"; import { ErrorProne } from "../parentClasses/ErrorProne"; import { OredicParser } from "./OredicParser"; @@ -27,14 +27,20 @@ export class OredicMatcher extends ErrorProne { private dump: string[]; constructor(private pack: OredicPack) { - super(); - this.parser = new OredicParser(); + super("OredicMatcher"); + this.parser = new OredicParser(pack); const dump = DUMPS.get(this.pack); - this.dump = dump ? dump.split("\n") : []; + this.dump = dump + ? dump.split("\n").filter((line) => line.trim() !== "") + : []; } - match(rules: exAstNode): Matches | null { - return this.parse(rules); + match(rules: AstNode): OredicMatches | StandardError { + const result = this.parse(rules); + if (result === null) { + return this.setError(500, "Failed to match oredics", "match"); + } + return result; } isUniqueMatch(pattern: string, targetIndex: number): boolean { @@ -89,7 +95,7 @@ export class OredicMatcher extends ErrorProne { * AST transformation pipeline */ - private parse(ast: exAst): string[] | null { + private parse(ast: MatcherNode | null): string[] | null { this.parsePatterns(ast); this.parseRegexes(ast); const finalNode = this.parseOredics(ast); @@ -97,7 +103,7 @@ export class OredicMatcher extends ErrorProne { return finalNode.children; } - private parsePatterns(ast: exAst): void { + private parsePatterns(ast: MatcherNode | null): void { if (!ast || ast.type === "regex" || ast.type === "oredic") return; if (ast.type === "pattern") { @@ -112,7 +118,7 @@ export class OredicMatcher extends ErrorProne { } } - private parseRegexes(ast: exAst): void { + private parseRegexes(ast: MatcherNode | null): void { if (!ast || ast.type === "pattern" || ast.type === "oredic") return; if (ast.type === "regex") { @@ -132,7 +138,7 @@ export class OredicMatcher extends ErrorProne { } } - private parseOredics(ast: exAst): OredicNode | null { + private parseOredics(ast: MatcherNode | null): OredicNode | null { if (!ast) return null; if (ast.type === "oredic") return ast; @@ -140,7 +146,8 @@ export class OredicMatcher extends ErrorProne { if (ast.type !== "operator") { this.setError( 500, - "Unknown Error: Got something else than Oredic and Operator Nodes in the final parse" + "Unknown Error: Got something else than Oredic and Operator Nodes in the final parse", + "parseOredics" ); return null; } @@ -210,7 +217,7 @@ export class OredicMatcher extends ErrorProne { return this.newOredicNode(oredicList); } - private applyConjunction(node: exAndNode): OredicNode { + private applyConjunction(node: MatcherAndNode): OredicNode { const occurences = new Map(); const toRemove = new Set(); let newChildren: string[] = []; @@ -220,7 +227,8 @@ export class OredicMatcher extends ErrorProne { if (child.type !== "oredic") { this.setError( 500, - "Unknown Error: Tried to apply conjunction on something else than an oredic node" + "Unknown Error: Tried to apply conjunction on something else than an oredic node", + "applyConjunction" ); return this.newOredicNode(null); } @@ -248,7 +256,8 @@ export class OredicMatcher extends ErrorProne { if (nonNegatedCount === 0) { this.setError( 500, - "AND operation requires at least one non-negated child" + "AND operation requires at least one non-negated child", + "applyConjunction" ); return this.newOredicNode(null); } @@ -262,14 +271,15 @@ export class OredicMatcher extends ErrorProne { return this.newOredicNode(newChildren); } - private applyDisjunction(node: exOrNode): OredicNode { + private applyDisjunction(node: MatcherOrNode): OredicNode { let newChildren: string[] = []; for (const child of node.children) { if (child.type !== "oredic") { this.setError( 500, - "Unknown Error: Tried to apply disjunction on something else than an oredic node" + "Unknown Error: Tried to apply disjunction on something else than an oredic node", + "applyDisjunction" ); return this.newOredicNode(null); } @@ -283,7 +293,7 @@ export class OredicMatcher extends ErrorProne { return this.newOredicNode(newChildren); } - private applyExclusiveDisjunction(node: exXorNode): OredicNode { + private applyExclusiveDisjunction(node: MatcherXorNode): OredicNode { const occurences = new Map(); let newChildren: string[] = []; @@ -291,7 +301,8 @@ export class OredicMatcher extends ErrorProne { if (child.type !== "oredic") { this.setError( 500, - "Unknown Error: Tried to apply xor on something else than an oredic node" + "Unknown Error: Tried to apply xor on something else than an oredic node", + "applyExclusiveDisjunction" ); return this.newOredicNode(null); } diff --git a/src/utils/parsers/OredicParser.ts b/src/utils/parsers/OredicParser.ts index 9277c8f..c547e16 100644 --- a/src/utils/parsers/OredicParser.ts +++ b/src/utils/parsers/OredicParser.ts @@ -3,9 +3,9 @@ * https://github.com/AE2-UEL/Applied-Energistics-2/blob/26bb5986c636e9bdde62559b0d1c2bbc48c4b9e3/src/main/java/appeng/util/item/OreDictFilterMatcher.java#L11 */ +import { OredicPack } from "../../config/routes"; import { StandardError } from "../../types/errors"; import { - Ast, AstNode, PatternNode, OperatorNode, @@ -15,17 +15,25 @@ import { NodeNames, LexemeElement, LexemeNames, - ParseState, + LexicalParseResult, } from "../../types/parsing"; import { ErrorProne } from "../parentClasses/ErrorProne"; +type ParseState = { + ast: AstNode | null; + tokenBuffer: Token[]; + operandBuffer: AstNode[]; + currentOperator: "AND" | "XOR" | "OR" | null; + negationFlag: boolean; +}; + export class OredicParser extends ErrorProne { private lexemeBuffer: string; private parenthesesCount: number; private optimizationPipeline: Array<(node: AstNode) => void>; - constructor() { - super(); + constructor(private pack: OredicPack) { + super("OredicParser"); this.lexemeBuffer = ""; this.parenthesesCount = 0; @@ -37,18 +45,25 @@ export class OredicParser extends ErrorProne { ]; } - parse(input: string): Ast | StandardError { + parse(input: string): AstNode | StandardError { this.resetState(); const { lexemeList } = this.lexicalParse(input.split("")); if (this.parenthesesCount !== 0) { - this.setError(400, "Unbalanced parentheses"); - return this.error; + return this.setError(400, "Unbalanced parentheses", "parse"); } const ast = this.parseNode(lexemeList); + if (this.isError(ast)) { + return this.propagateError(ast, "Failed to parse node", "parse"); + } + + if (!ast) { + return this.setError(400, "Failed to parse input", "parse"); + } + if (this.error.status) { return this.error; } @@ -140,7 +155,7 @@ export class OredicParser extends ErrorProne { return { lexemeList, consumed: pattern.length }; } - private parseNode(lexemeList: LexemeElement[]): Ast { + private parseNode(lexemeList: LexemeElement[]): AstNode | null { if (this.error.status) { return null; } @@ -158,17 +173,17 @@ export class OredicParser extends ErrorProne { case "group": const subLexeme = lexemeList[i].content; if (typeof subLexeme === "string" || !subLexeme) { - this.setError(400, "Invalid group content"); + this.setError(400, "Invalid group content", "parseNode"); return null; } - const subAst: Ast = this.parseNode(subLexeme); + const subAst: AstNode | null = this.parseNode(subLexeme); if (this.error.status) { return null; } if (!subAst) { - this.setError(400, "Failed to parse group"); + this.setError(400, "Failed to parse group", "parseNode"); return null; } @@ -188,7 +203,7 @@ export class OredicParser extends ErrorProne { case "operator": const operator = lexemeList[i].content; if (operator !== "AND" && operator !== "OR" && operator !== "XOR") { - this.setError(400, "Invalid operator"); + this.setError(400, "Invalid operator", "parseNode"); return null; } @@ -203,7 +218,11 @@ export class OredicParser extends ErrorProne { case "negation": if (state.tokenBuffer.length !== 0) { - this.setError(400, "Negation must come before pattern tokens"); + this.setError( + 400, + "Negation must come before pattern tokens", + "parseNode" + ); return null; } state.negationFlag = !state.negationFlag; @@ -216,7 +235,7 @@ export class OredicParser extends ErrorProne { case "text": const content = lexemeList[i].content; if (typeof content !== "string") { - this.setError(400, "Invalid text content"); + this.setError(400, "Invalid text content", "parseNode"); return null; } state.tokenBuffer.push({ type: NodeNames.TEXT, content: content }); @@ -227,13 +246,13 @@ export class OredicParser extends ErrorProne { return this.flushEnd(state); } - private flattenAst(ast: Ast): void { + private flattenAst(ast: AstNode): void { if (!ast) { return; } let currentAst = ast; - let previousAst: Ast = null; + let previousAst: AstNode | null = null; while (JSON.stringify(currentAst) !== JSON.stringify(previousAst)) { previousAst = JSON.parse(JSON.stringify(currentAst)); @@ -318,7 +337,7 @@ export class OredicParser extends ErrorProne { state.operandBuffer = []; } - private flushEnd(state: ParseState): Ast { + private flushEnd(state: ParseState): AstNode | null { if (state.tokenBuffer.length > 0) { const pattern = this.createPatternNode( state.tokenBuffer, @@ -370,7 +389,11 @@ export class OredicParser extends ErrorProne { private decrementParentheses(): void { if (this.parenthesesCount === 0) { - this.setError(400, "Closing parenthesis without opening"); + this.setError( + 400, + "Closing parenthesis without opening", + "decrementParentheses" + ); return; } this.parenthesesCount -= 1; diff --git a/src/utils/parsers/OredicShortener.ts b/src/utils/parsers/OredicShortener.ts index 206f752..a01af5f 100644 --- a/src/utils/parsers/OredicShortener.ts +++ b/src/utils/parsers/OredicShortener.ts @@ -1,5 +1,6 @@ import { DUMPS } from "../../loaders/storage"; -import { Ast, NodeNames } from "../../types/parsing"; +import { AstNode, NodeNames } from "../../types/parsing"; +import { StandardError } from "../../types/errors"; import { TimerRes } from "../../types/timer"; import { getLogger } from "../Logger"; import { ErrorProne } from "../parentClasses/ErrorProne"; @@ -25,22 +26,22 @@ export class OredicShortener extends ErrorProne { private shortcuts: Map; constructor(private pack: OredicPack) { - super(); + super("OredicShortener"); this.dump = this.loadDump(); this.matcher = new OredicMatcher(this.pack); this.telemetry = new Map(); this.shortcuts = new Map(); } - getShortcuts() { + getShortcuts(): Map | StandardError { //TODO: Implement caching strategy!!! - if (!this.shortcuts) { - this.shortcuts = this.findAll(); - } - - if (!this.shortcuts) { - throw new Error("Shortcuts didn't generate"); + if (this.shortcuts.size === 0) { + const result = this.findAll(); + if (this.isError(result)) { + return this.propagateError(result, "Failed to generate shortcuts", "getShortcuts"); + } + this.shortcuts = result; } return this.shortcuts; diff --git a/storage/oredic/dumps/.gitkeep b/storage/oredic/dumps/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/storage/oredic/dumps/nomi-ceu.txt b/storage/oredic/dumps/nomi-ceu.txt deleted file mode 100644 index 9e502e3..0000000 --- a/storage/oredic/dumps/nomi-ceu.txt +++ /dev/null @@ -1,7443 +0,0 @@ -logWood -plankWood -slabWood -stairWood -fenceWood -fenceGateWood -doorWood -stickWood -treeSapling -treeLeaves -vine -oreGold -oreIron -oreLapis -oreDiamond -oreRedstone -oreEmerald -oreQuartz -oreCoal -ingotIron -ingotGold -ingotBrick -ingotBrickNether -nuggetGold -nuggetIron -gemDiamond -gemEmerald -gemQuartz -gemPrismarine -dustPrismarine -dustRedstone -dustGlowstone -gemLapis -blockGold -blockIron -blockLapis -blockDiamond -blockRedstone -blockEmerald -blockQuartz -blockCoal -cropWheat -cropPotato -cropCarrot -cropNetherWart -sugarcane -blockCactus -dye -paper -slimeball -enderpearl -bone -gunpowder -string -netherStar -leather -feather -egg -record -dirt -grass -stone -cobblestone -gravel -sand -sandstone -netherrack -obsidian -glowstone -endstone -torch -workbench -blockSlime -blockPrismarine -blockPrismarineBrick -blockPrismarineDark -stoneGranite -stoneGranitePolished -stoneDiorite -stoneDioritePolished -stoneAndesite -stoneAndesitePolished -blockGlassColorless -blockGlass -paneGlassColorless -paneGlass -wool -chest -chestWood -chestEnder -chestTrapped -dyeBlack -woolBlack -blockGlassBlack -paneGlassBlack -dyeRed -woolRed -blockGlassRed -paneGlassRed -dyeGreen -woolGreen -blockGlassGreen -paneGlassGreen -dyeBrown -woolBrown -blockGlassBrown -paneGlassBrown -dyeBlue -woolBlue -blockGlassBlue -paneGlassBlue -dyePurple -woolPurple -blockGlassPurple -paneGlassPurple -dyeCyan -woolCyan -blockGlassCyan -paneGlassCyan -dyeLightGray -woolLightGray -blockGlassLightGray -paneGlassLightGray -dyeGray -woolGray -blockGlassGray -paneGlassGray -dyePink -woolPink -blockGlassPink -paneGlassPink -dyeLime -woolLime -blockGlassLime -paneGlassLime -dyeYellow -woolYellow -blockGlassYellow -paneGlassYellow -dyeLightBlue -woolLightBlue -blockGlassLightBlue -paneGlassLightBlue -dyeMagenta -woolMagenta -blockGlassMagenta -paneGlassMagenta -dyeOrange -woolOrange -blockGlassOrange -paneGlassOrange -dyeWhite -woolWhite -blockGlassWhite -paneGlassWhite -blockGlowstone -blockWool -coal -charcoal -dustBlaze -rodBlaze -cropBeetroot -cropMelon -cropPumpkin -seedBeetroot -seedMelon -seedPumpkin -seedWheat -cofh:potion -dyeSilver -slabWoodOak -slabWoodSpruce -slabWoodBirch -slabWoodJungle -slabWoodAcacia -slabWoodDarkOak -slabStone -slabSandstone -slabCobblestone -slabBricks -slabStoneBricks -slabNetherBrick -slabQuartz -blockWoolWhite -blockWoolOrange -blockWoolMagenta -blockWoolLightBlue -blockWoolYellow -blockWoolLime -blockWoolPink -blockWoolGray -blockWoolLightGray -blockWoolCyan -blockWoolPurple -blockWoolBlue -blockWoolBrown -blockWoolGreen -blockWoolRed -blockWoolBlack -oreCopper -oreTin -oreSilver -oreLead -oreAluminum -oreNickel -orePlatinum -oreIridium -oreMithril -oreClathrateOilSand -oreClathrateOilShale -oreClathrateRedstone -oreClathrateGlowstone -oreClathrateEnder -blockCopper -blockTin -blockSilver -blockLead -blockAluminum -blockNickel -blockPlatinum -blockIridium -blockMithril -blockSteel -blockElectrum -blockInvar -blockBronze -blockConstantan -blockSignalum -blockLumium -blockEnderium -blockCharcoal -blockFuelCoke -blockGlassHardened -blockRockwool -coinIron -coinGold -coinCopper -coinTin -coinSilver -coinLead -coinAluminum -coinNickel -coinPlatinum -coinIridium -coinMithril -coinSteel -coinElectrum -coinInvar -coinBronze -coinConstantan -coinSignalum -coinLumium -coinEnderium -dustIron -dustGold -nuggetDiamond -nuggetEmerald -gearWood -gearStone -gearIron -gearGold -gearDiamond -gearEmerald -plateIron -plateGold -dustCopper -dustTin -dustSilver -dustLead -dustAluminum -dustNickel -dustPlatinum -dustIridium -dustMithril -dustSteel -dustElectrum -dustInvar -dustBronze -dustConstantan -dustSignalum -dustLumium -dustEnderium -ingotCopper -ingotTin -ingotSilver -ingotLead -ingotAluminum -ingotNickel -ingotPlatinum -ingotIridium -ingotMithril -ingotSteel -ingotElectrum -ingotInvar -ingotBronze -ingotConstantan -ingotSignalum -ingotLumium -ingotEnderium -nuggetCopper -nuggetTin -nuggetSilver -nuggetLead -nuggetAluminum -nuggetNickel -nuggetPlatinum -nuggetIridium -nuggetMithril -nuggetSteel -nuggetElectrum -nuggetInvar -nuggetBronze -nuggetConstantan -nuggetSignalum -nuggetLumium -nuggetEnderium -gearCopper -gearTin -gearSilver -gearLead -gearAluminum -gearNickel -gearPlatinum -gearIridium -gearMithril -gearSteel -gearElectrum -gearInvar -gearBronze -gearConstantan -gearSignalum -gearLumium -gearEnderium -plateCopper -plateTin -plateSilver -plateLead -plateAluminum -plateNickel -platePlatinum -plateIridium -plateMithril -plateSteel -plateElectrum -plateInvar -plateBronze -plateConstantan -plateSignalum -plateLumium -plateEnderium -dustCoal -dustCharcoal -dustObsidian -dustSulfur -dustWood -fuelCoke -crystalSlag -crystalSlagRich -crystalCinnabar -crystalCrudeOil -crystalRedstone -crystalGlowstone -crystalEnder -dustPyrotheum -dustCryotheum -dustAerotheum -dustPetrotheum -dustMana -rodBlizz -dustBlizz -rodBlitz -dustBlitz -rodBasalz -dustBasalz -dustSaltpeter -itemSawdust -itemBiomass -itemBioblend -itemBiomassRich -itemBioblendRich -itemSlag -itemSlagRich -itemCinnabar -clathrateOil -clathrateRedstone -clathrateGlowstone -clathrateEnder -dragonEgg -oreDraconium -blockDraconium -blockDraconiumAwakened -ingotDraconium -dustDraconium -ingotDraconiumAwakened -nuggetDraconium -nuggetDraconiumAwakened -blockCrystalMatrix -blockCosmicNeutronium -blockInfinity -ingotCrystalMatrix -ingotCosmicNeutronium -ingotInfinity -nuggetCosmicNeutronium - -ingotUnstable -nuggetUnstable -compressed1xCobblestone -compressed2xCobblestone -compressed3xCobblestone -compressed4xCobblestone -compressed5xCobblestone -compressed6xCobblestone -compressed7xCobblestone -compressed8xCobblestone -compressed1xDirt -compressed2xDirt -compressed3xDirt -compressed4xDirt -compressed1xSand -compressed2xSand -compressed1xGravel -compressed2xGravel -compressed1xNetherrack -compressed2xNetherrack -compressed3xNetherrack -compressed4xNetherrack -compressed5xNetherrack -compressed6xNetherrack -gemRedstone -gearRedstone -eyeofredstone -dustLunar -coalPowered -gemMoon -xuUpgradeSpeed -xuUpgradeStack -xuUpgradeMining -xuUpgradeBlank -dropofevil -ingotDemonicMetal -ingotEnchantedMetal -xuRedstoneCoil -xuUpgradeSpeedEnchanted -ingotEvilMetal -blockEnchantedMetal -blockDemonicMetal -blockEvilMetal -blockMagicalWood -bookshelf -bricksStone -rodStone -projredIllumar -gemRuby -gemSapphire -gemPeridot -ingotRedAlloy -ingotElectrotineAlloy -dustElectrotine -gearDiamatine -gearEmeradic -gearEnori -gearPalis -gearRestonia -gearVoid -gearDiamatineEmpowered -gearEmeradicEmpowered -gearEnoriEmpowered -gearPalisEmpowered -gearRestoniaEmpowered -gearVoidEmpowered -plateFluix -plateInfinity -plateDemonicMetal -blockGraphite -blockBeryllium -blockZirconium -blockManganese -blockFissionModerator -blockThorium230 -blockUranium238 -blockNeptunium237 -blockPlutonium242 -blockAmericium243 -blockCurium246 -blockBerkelium247 -blockCalifornium252 -ingotGraphite -ingotBeryllium -ingotZirconium -ingotManganese -ingotThoriumOxide -ingotUraniumOxide -ingotManganeseOxide -ingotManganeseDioxide -dustGraphite -dustBeryllium -dustZirconium -dustManganese -dustThoriumOxide -dustUraniumOxide -dustManganeseOxide -dustManganeseDioxide -gemRhodochrosite -gemBoronNitride -gemFluorite -gemVilliaumite -gemCarobbiite -gemBoronArsenide -itemSilicon -dustDiamond -dustRhodochrosite -dustQuartz -dustNetherQuartz -dustBoronNitride -dustFluorite -dustVilliaumite -dustCarobbiite -dustArsenic -dustEndstone -ingotTough -ingotHardCarbon -ingotMagnesiumDiboride -ingotLithiumManganeseDioxide -ingotFerroboron -ingotShibuichi -ingotTinSilver -ingotLeadPlatinum -ingotExtreme -ingotThermoconducting -ingotZircaloy -ingotSiliconCarbide -ingotSiCSiCCMC -ingotHSLASteel -dustCalciumSulfate -dustCrystalBinder -dustEnergetic -dustSodiumFluoride -dustPotassiumFluoride -dustSodiumHydroxide -dustPotassiumHydroxide -dustBorax -dustDimensional -dustCarbonManganese -dustAlugentum -plateBasic -plateAdvanced -plateDU -plateElite -solenoidCopper -solenoidMagnesiumDiboride -bioplastic -servo -motor -actuator -chassis -emptyFrame -steelFrame -fiberSiliconCarbide -tinyDustLead -ingotThorium230Base -ingotThorium230 -ingotThorium230Oxide -nuggetThorium230 -nuggetThorium230Oxide -ingotThorium232Base -ingotThorium232 -ingotThorium232Oxide -nuggetThorium232 -nuggetThorium232Oxide -ingotUranium233Base -ingotUranium233 -ingotUranium233Oxide -nuggetUranium233 -nuggetUranium233Oxide -ingotUranium235Base -ingotUranium235 -ingotUranium235Oxide -nuggetUranium235 -nuggetUranium235Oxide -ingotUranium238Base -ingotUranium238 -ingotUranium238Oxide -nuggetUranium238 -nuggetUranium238Oxide -ingotNeptunium236Base -ingotNeptunium236 -ingotNeptunium236Oxide -nuggetNeptunium236 -nuggetNeptunium236Oxide -ingotNeptunium237Base -ingotNeptunium237 -ingotNeptunium237Oxide -nuggetNeptunium237 -nuggetNeptunium237Oxide -ingotPlutonium238Base -ingotPlutonium238 -ingotPlutonium238Oxide -nuggetPlutonium238 -nuggetPlutonium238Oxide -ingotPlutonium239Base -ingotPlutonium239 -ingotPlutonium239Oxide -nuggetPlutonium239 -nuggetPlutonium239Oxide -ingotPlutonium241Base -ingotPlutonium241 -ingotPlutonium241Oxide -nuggetPlutonium241 -nuggetPlutonium241Oxide -ingotPlutonium242Base -ingotPlutonium242 -ingotPlutonium242Oxide -nuggetPlutonium242 -nuggetPlutonium242Oxide -ingotAmericium241Base -ingotAmericium241 -ingotAmericium241Oxide -nuggetAmericium241 -nuggetAmericium241Oxide -ingotAmericium242Base -ingotAmericium242 -ingotAmericium242Oxide -nuggetAmericium242 -nuggetAmericium242Oxide -ingotAmericium243Base -ingotAmericium243 -ingotAmericium243Oxide -nuggetAmericium243 -nuggetAmericium243Oxide -ingotCurium243Base -ingotCurium243 -ingotCurium243Oxide -nuggetCurium243 -nuggetCurium243Oxide -ingotCurium245Base -ingotCurium245 -ingotCurium245Oxide -nuggetCurium245 -nuggetCurium245Oxide -ingotCurium246Base -ingotCurium246 -ingotCurium246Oxide -nuggetCurium246 -nuggetCurium246Oxide -ingotCurium247Base -ingotCurium247 -ingotCurium247Oxide -nuggetCurium247 -nuggetCurium247Oxide -ingotBerkelium247Base -ingotBerkelium247 -ingotBerkelium247Oxide -nuggetBerkelium247 -nuggetBerkelium247Oxide -ingotBerkelium248Base -ingotBerkelium248 -ingotBerkelium248Oxide -nuggetBerkelium248 -nuggetBerkelium248Oxide -ingotCalifornium249Base -ingotCalifornium249 -ingotCalifornium249Oxide -nuggetCalifornium249 -nuggetCalifornium249Oxide -ingotCalifornium250Base -ingotCalifornium250 -ingotCalifornium250Oxide -nuggetCalifornium250 -nuggetCalifornium250Oxide -ingotCalifornium251Base -ingotCalifornium251 -ingotCalifornium251Oxide -nuggetCalifornium251 -nuggetCalifornium251Oxide -ingotCalifornium252Base -ingotCalifornium252 -ingotCalifornium252Oxide -nuggetCalifornium252 -nuggetCalifornium252Oxide -fuelTBU -fuelTBUOxide -fuelLEU233 -fuelLEU233Oxide -fuelHEU233 -fuelHEU233Oxide -fuelLEU235 -fuelLEU235Oxide -fuelHEU235 -fuelHEU235Oxide -fuelLEN236 -fuelLEN236Oxide -fuelHEN236 -fuelHEN236Oxide -fuelLEP239 -fuelLEP239Oxide -fuelHEP239 -fuelHEP239Oxide -fuelLEP241 -fuelLEP241Oxide -fuelHEP241 -fuelHEP241Oxide -fuelMOX239 -fuelMOX241 -fuelLEA242 -fuelLEA242Oxide -fuelHEA242 -fuelHEA242Oxide -fuelLECm243 -fuelLECm243Oxide -fuelHECm243 -fuelHECm243Oxide -fuelLECm245 -fuelLECm245Oxide -fuelHECm245 -fuelHECm245Oxide -fuelLECm247 -fuelLECm247Oxide -fuelHECm247 -fuelHECm247Oxide -fuelLEB248 -fuelLEB248Oxide -fuelHEB248 -fuelHEB248Oxide -fuelLECf249 -fuelLECf249Oxide -fuelHECf249 -fuelHECf249Oxide -fuelLECf251 -fuelLECf251Oxide -fuelHECf251 -fuelHECf251Oxide -depletedFuelTBU -depletedFuelTBUOxide -depletedFuelLEU233 -depletedFuelLEU233Oxide -depletedFuelHEU233 -depletedFuelHEU233Oxide -depletedFuelLEU235 -depletedFuelLEU235Oxide -depletedFuelHEU235 -depletedFuelHEU235Oxide -depletedFuelLEN236 -depletedFuelLEN236Oxide -depletedFuelHEN236 -depletedFuelHEN236Oxide -depletedFuelLEP239 -depletedFuelLEP239Oxide -depletedFuelHEP239 -depletedFuelHEP239Oxide -depletedFuelLEP241 -depletedFuelLEP241Oxide -depletedFuelHEP241 -depletedFuelHEP241Oxide -depletedFuelMOX239 -depletedFuelMOX241 -depletedFuelLEA242 -depletedFuelLEA242Oxide -depletedFuelHEA242 -depletedFuelHEA242Oxide -depletedFuelLECm243 -depletedFuelLECm243Oxide -depletedFuelHECm243 -depletedFuelHECm243Oxide -depletedFuelLECm245 -depletedFuelLECm245Oxide -depletedFuelHECm245 -depletedFuelHECm245Oxide -depletedFuelLECm247 -depletedFuelLECm247Oxide -depletedFuelHECm247 -depletedFuelHECm247Oxide -depletedFuelLEB248 -depletedFuelLEB248Oxide -depletedFuelHEB248 -depletedFuelHEB248Oxide -depletedFuelLECf249 -depletedFuelLECf249Oxide -depletedFuelHECf249 -depletedFuelHECf249Oxide -depletedFuelLECf251 -depletedFuelLECf251Oxide -depletedFuelHECf251 -depletedFuelHECf251Oxide -depletedFuelIC2U -depletedFuelIC2MOX -ingotBoron10 -nuggetBoron10 -ingotBoron11 -nuggetBoron11 -ingotLithium6 -nuggetLithium6 -ingotLithium7 -nuggetLithium7 -gearDominos -dustWheat -dustCocoa -ingotCocoaButter -ingotUnsweetenedChocolate -ingotDarkChocolate -ingotChocolate -ingotMarshmallow -projredInsFramedWire -projredInsulatedWire -projredBundledCable -blockElectrumFlux -blockCrystalFlux -dustElectrumFlux -ingotElectrumFlux -nuggetElectrumFlux -gearElectrumFlux -plateElectrumFlux -gemCrystalFlux -dustDilithium -gemDilithium -oreDilithium -stickIron -sheetIron -coilGold -blockCoil -dustSilicon -ingotSilicon -bouleSilicon -nuggetSilicon -plateSilicon -stickCopper -sheetCopper -coilCopper -stickSteel -fanSteel -sheetSteel -dustTitanium -ingotTitanium -nuggetTitanium -plateTitanium -stickTitanium -sheetTitanium -gearTitanium -blockTitanium -coilTitanium -oreRutile -oreTitanium -sheetAluminum -coilAluminum -stickIridium -coilIridium -blockMotor -dustTitaniumAluminide -ingotTitaniumAluminide -nuggetTitaniumAluminide -plateTitaniumAluminide -stickTitaniumAluminide -sheetTitaniumAluminide -gearTitaniumAluminide -blockTitaniumAluminide -dustTitaniumIridium -ingotTitaniumIridium -nuggetTitaniumIridium -plateTitaniumIridium -stickTitaniumIridium -sheetTitaniumIridium -gearTitaniumIridium -blockTitaniumIridium -turfMoon -concrete -stoneBasalt -stoneBasaltPolished -bookshelfOak -bookshelfSpruce -bookshelfBirch -bookshelfJungle -bookshelfAcacia -bookshelfDarkOak -blockCobalt -blockCoalCoke -blockMossy -blockConcrete -blockConcreteBlack -blockConcreteRed -blockConcreteGreen -blockConcreteBrown -blockConcreteBlue -blockConcretePurple -blockConcreteCyan -blockConcreteLightGray -blockConcreteGray -blockConcretePink -blockConcreteLime -blockConcreteYellow -blockConcreteLightBlue -blockConcreteMagenta -blockConcreteOrange -blockConcreteWhite -hardenedClay -ice -blockIce -stoneLimestone -stoneLimestonePolished -stoneMarble -stoneMarblePolished -prismarine -prismarineBrick -prismarineDark -brickStone -blockUranium -itemBattery -waferSilicon -ingotCarbon -itemLens -dustThermite -slab -blockWarpCoreCore -blockWarpCoreRim -itemQuartzWrench -itemQuartzKnife -crystalCertusQuartz -dustCertusQuartz -crystalFluix -dustFluix -pearlFluix -crystalPureCertusQuartz -crystalPureNetherQuartz -crystalPureFluix -dustEnder -dustEnderPearl -itemIlluminatedPanel -blockElevator -blockBlackIron -blockLuminessence -blockNetherStar -blockCrystaltine -blockUltimate -ingotBlackIron -ingotUltimate -nuggetNetherStar -nuggetUltimate -nuggetCrystaltine -nuggetBlackIron -ingotCrystaltine -drawerBasic -drawerTrim -circuitUlv -componentTransistor -componentResistor -componentCapacitor -componentDiode -componentInductor -circuitLv -circuitMv -circuitHv -circuitEv -circuitIv -circuitLuv -circuitZpm -circuitUv -circuitUhv -batteryUlv -batteryLv -batteryMv -batteryHv -batteryEv -batteryIv -batteryLuv -batteryZpm -batteryUv -batteryUhv -dustEmerald -dustLapis -dustQuartzBlack -oreQuartzBlack -gemQuartzBlack -seedCanola -cropCanola -seedRice -cropRice -seedFlax -cropFlax -seedCoffee -cropCoffee -blockQuartzBlack -oreArdite -oreCobalt -itemSimpleMachineChassi -itemMachineChassi -itemChassiParts -itemPlatePhotovoltaic -itemConduitBinder -itemGliderWing -itemGliderWings -itemNutritiousStick -gearIronInfinity -gearEnergized -gearVibrant -itemPulsatingCrystal -itemVibrantCrystal -itemEnderCrystal -itemAttractorCrystal -itemWeatherCrystal -itemPrecientCrystal -dustBedrock -itemBinderComposite -nuggetEnderpearl -itemPrecientPowder -itemVibrantPowder -itemPulsatingPowder -itemEnderCrystalPowder -itemPowderPhotovoltaic -skullZombieElectrode -skullZombieController -skullZombieFrankenstein -skullEnderResonator -skullSentientEnder -skullSkeletalContractor -itemPlantgreen -itemPlantbrown -dyeMachine -dyeSoulMachine -itemSoulMachineChassi -itemEnhancedMachineChassi -itemUnsouledMachineChassi -skullGuardianDiode -itemGrindingBallSignalum -itemGrindingBallEnderium -itemGrindingBallLumium -itemRedstoneFilterBase -itemConfusingDust -itemEnderFragment -itemWitheringDust -itemRemoteAwarenessUpgrade -itemWirelessDish -itemEndSteelMachineChassi -dyeEnhancedMachine -itemEnhancedChassiParts -itemSimpleChassiParts -itemCakeBase -itemInfinityRod -ingotBrickNetherGlazed -gearDark -dustSoularium -itemInfinityGoop -itemClayedGlowstone -paperBlack -itemTokenAnimal -itemTokenMonster -itemTokenPlayer -itemDeathUrnUnfired -blockElectricalSteel -ingotElectricalSteel -nuggetElectricalSteel -ballElectricalSteel -blockEnergeticAlloy -ingotEnergeticAlloy -nuggetEnergeticAlloy -ballEnergeticAlloy -blockVibrantAlloy -ingotVibrantAlloy -nuggetVibrantAlloy -ballVibrantAlloy -blockRedstoneAlloy -ingotRedstoneAlloy -nuggetRedstoneAlloy -ballRedstoneAlloy -blockConductiveIron -ingotConductiveIron -nuggetConductiveIron -ballConductiveIron -blockPulsatingIron -ingotPulsatingIron -nuggetPulsatingIron -ballPulsatingIron -blockDarkSteel -ingotDarkSteel -nuggetDarkSteel -ballDarkSteel -blockSoularium -ingotSoularium -nuggetSoularium -ballSoularium -blockEndSteel -ingotEndSteel -nuggetEndSteel -ballEndSteel -blockConstructionAlloy -ingotConstructionAlloy -nuggetConstructionAlloy -ballConstructionAlloy -blockCrudeSteel -ingotCrudeSteel -nuggetCrudeSteel -ballCrudeSteel -blockCrystallineAlloy -ingotCrystallineAlloy -nuggetCrystallineAlloy -ballCrystallineAlloy -blockMelodicAlloy -ingotMelodicAlloy -nuggetMelodicAlloy -ballMelodicAlloy -blockStellarAlloy -ingotStellarAlloy -nuggetStellarAlloy -ballStellarAlloy -blockCrystallinePinkSlime -ingotCrystallinePinkSlime -nuggetCrystallinePinkSlime -ballCrystallinePinkSlime -blockEnergeticSilver -ingotEnergeticSilver -nuggetEnergeticSilver -ballEnergeticSilver -blockVividAlloy -ingotVividAlloy -nuggetVividAlloy -ballVividAlloy -blockGlassHardenedBlack -blockGlassHardenedRed -blockGlassHardenedGreen -blockGlassHardenedBrown -blockGlassHardenedBlue -blockGlassHardenedPurple -blockGlassHardenedCyan -blockGlassHardenedLightGray -blockGlassHardenedGray -blockGlassHardenedPink -blockGlassHardenedLime -blockGlassHardenedYellow -blockGlassHardenedLightBlue -blockGlassHardenedMagenta -blockGlassHardenedOrange -blockGlassHardenedWhite -fusedQuartz -fusedGlass -enlightenedFusedQuartz -enlightenedFusedGlass -darkFusedQuartz -darkFusedGlass -holyFusedQuartz -holyFusedGlass -holyEnlightenedFusedQuartz -holyEnlightenedFusedGlass -holyDarkFusedQuartz -holyDarkFusedGlass -unholyFusedQuartz -unholyFusedGlass -unholyEnlightenedFusedQuartz -unholyEnlightenedFusedGlass -unholyDarkFusedQuartz -unholyDarkFusedGlass -pastureFusedQuartz -pastureFusedGlass -pastureEnlightenedFusedQuartz -pastureEnlightenedFusedGlass -pastureDarkFusedQuartz -pastureDarkFusedGlass -notHolyFusedQuartz -notHolyFusedGlass -notHolyEnlightenedFusedQuartz -notHolyEnlightenedFusedGlass -notHolyDarkFusedQuartz -notHolyDarkFusedGlass -notUnholyFusedQuartz -notUnholyFusedGlass -notUnholyEnlightenedFusedQuartz -notUnholyEnlightenedFusedGlass -notUnholyDarkFusedQuartz -notUnholyDarkFusedGlass -notPastureFusedQuartz -notPastureFusedGlass -notPastureEnlightenedFusedQuartz -notPastureEnlightenedFusedGlass -notPastureDarkFusedQuartz -notPastureDarkFusedGlass -itemSkull -toolHoe -eggOwl -toolShears -toolTreetap -compressed1xDustBedrock -compressed2xDustBedrock -compressed3xDustBedrock -rodIron -rodTitanium -rodSteel -rodTitaniumAluminide -rodCopper -ingotBrass -ingotAluminium -glass -ingotElvenElementium -ingotGaia -ingotManasteel -ingotTerrasteel -gemAmethyst -ingotEnrichedGold -gemTanzanite -ingotOsmium -ingotRefinedGlowstone -ingotRefinedObsidian -ingotInsanium -ingotInferium -ingotIntermedium -ingotPrudentium -ingotSoulium -ingotSuperium -ingotSupremium -ingotAlumite -ingotMirion -ingotOsgloglas -ingotOsmiridium -ingotIronCompressed -ingotCorrupted -gemAmber -quicksilver -ingotArdite -ingotCobalt -ingotKnightslime -ingotManyullyn -ingotPigiron -ingotFiery -ingotIronwood -ingotKnightmetal -gemPearl -blockPearl -bed -dustAwakenedDraconium -dustDarkSteel -dustOmnium -dustArdite -dustManyullyn -dustConductiveIron -dustEnergeticAlloy -dustVibrantAlloy -dustPulsatingIron -dustElectricalSteel -dustCrystalMatrix -dustEndSteel -dustInfinity -dustMicroversium -dustDraconicSuperconductor -dustOsmiridium8020 -dustIridosmine8020 -dustKaemanite -dustTungstenTrioxide -dustBerylliumOxide -dustNiobiumPentoxide -dustTantalumPentoxide -dustManganeseDifluoride -dustMolybdenumTrioxide -dustLeadChloride -dustWollastonite -dustSodiumMetavanadate -dustVanadiumPentoxide -dustAmmoniumMetavanadate -dustPhthalicAnhydride -dustEthylanthraquinone -dustGrapheneOxide -dustKaptonK -dustDurene -dustPyromelliticDianhydride -dustOxydianiline -dustNaquadahOxide -dustPyromorphite -dustNaquadahHydroxide -dustSnowchestite -dustCaesiumHydroxide -dustLeadMetasilicate -dustPlatinumMetallic -dustPalladiumMetallic -dustAmmoniumHexachloroplatinate -dustPotassiumBisulfate -dustPotassiumPyrosulfate -dustPotassiumSulfate -dustZincSulfate -dustSodiumNitrate -dustRhodiumNitrate -dustSodiumRuthenate -dustSodiumPeroxide -dustIridiumDioxideResidue -dustAmmoniumHexachloroiridiate -dustPlatinumGroupResidue -dustCrudePlatinumResidue -dustCrudePalladiumResidue -dustIridiumGroupSludge -dustCrudeRhodiumResidue -dustRhodiumSalt -dustSodiumMethoxide -dustStoneResidue -dustUncommonResidue -dustOxidisedResidue -dustRefinedResidue -dustCleanInertResidue -dustTaranium -dustDarmstadtite -dustDulysite -dustLaurite -dustCuprorhodsite -dustAluminium -dustAmericium -dustAntimony -dustBarium -dustBerkelium -dustBismuth -dustBoron -dustCaesium -dustCalcium -dustCalifornium -dustCarbon -dustCadmium -dustCerium -dustChrome -dustCobalt -dustCurium -dustDarmstadtium -dustEinsteinium -dustEuropium -dustGallium -dustIndium -dustLanthanum -dustLithium -dustLutetium -dustMagnesium -dustMolybdenum -dustNeodymium -dustNeptunium -dustNiobium -dustOsmium -dustPalladium -dustPhosphorus -dustPlutonium -dustPlutonium239 -dustPlutonium241 -dustPotassium -dustRhodium -dustRuthenium -dustSamarium -dustSodium -dustTantalum -dustThorium -dustTungsten -dustUranium -dustUranium238 -dustUranium235 -dustVanadium -dustYttrium -dustZinc -dustNaquadah -dustNaquadahEnriched -dustNaquadria -dustNeutronium -dustTritanium -dustDuranium -dustTrinium -dustAlmandine -dustAndradite -dustAnnealedCopper -dustAsbestos -dustAsh -dustBandedIron -dustBatteryAlloy -dustBlueTopaz -dustBrass -dustBrownLimonite -dustCalcite -dustCassiterite -dustCassiteriteSand -dustChalcopyrite -dustChromite -dustCinnabar -dustCobaltite -dustCooperite -dustCupronickel -dustDarkAsh -dustGalena -dustGarnierite -dustGreenSapphire -dustGrossular -dustIce -dustIlmenite -dustRutile -dustBauxite -dustKanthal -dustLazurite -dustMagnalium -dustMagnesite -dustMagnetite -dustMolybdenite -dustNichrome -dustNiobiumNitride -dustNiobiumTitanium -dustPhosphate -dustPlatinumRaw -dustSterlingSilver -dustRoseGold -dustBlackBronze -dustBismuthBronze -dustBiotite -dustPowellite -dustPyrite -dustPyrolusite -dustPyrope -dustRockSalt -dustRuridit -dustRuby -dustSalt -dustSapphire -dustScheelite -dustSodalite -dustAluminiumSulfite -dustTantalite -dustCoke -dustSolderingAlloy -dustSpessartine -dustSphalerite -dustStainlessSteel -dustStibnite -dustTetrahedrite -dustTinAlloy -dustTopaz -dustTungstate -dustUltimet -dustUraninite -dustUvarovite -dustVanadiumGallium -dustWroughtIron -dustWulfenite -dustYellowLimonite -dustYttriumBariumCuprate -dustQuartzite -dustGraphene -dustTungsticAcid -dustOsmiridium -dustLithiumChloride -dustCalciumChloride -dustBornite -dustChalcocite -dustGalliumArsenide -dustPotash -dustSodaAsh -dustIndiumGalliumPhosphide -dustNickelZincFerrite -dustSiliconDioxide -dustMagnesiumChloride -dustSodiumSulfide -dustPhosphorusPentoxide -dustQuicklime -dustSodiumBisulfate -dustFerriteMixture -dustMagnesia -dustPlatinumGroupSludge -dustRealgar -dustSodiumBicarbonate -dustPotassiumDichromate -dustChromiumTrioxide -dustAntimonyTrioxide -dustZincite -dustCupricOxide -dustCobaltOxide -dustArsenicTrioxide -dustMassicot -dustFerrosilite -dustMetalMixture -dustBastnasite -dustPentlandite -dustSpodumene -dustLepidolite -dustGlauconiteSand -dustMalachite -dustMica -dustBarite -dustAlunite -dustTalc -dustSoapstone -dustKyanite -dustIronMagnetic -dustTungstenCarbide -dustPotassiumFeldspar -dustNeodymiumMagnetic -dustSamariumMagnetic -dustManganesePhosphide -dustMagnesiumDiboride -dustMercuryBariumCalciumCuprate -dustUraniumTriplatinum -dustSamariumIronArsenicOxide -dustIndiumTinBariumTitaniumCuprate -dustUraniumRhodiumDinaquadide -dustEnrichedNaquadahTriniumEuropiumDuranide -dustRutheniumTriniumAmericiumNeutronate -dustInertMetalMixture -dustRhodiumSulfate -dustRutheniumTetroxide -dustOsmiumTetroxide -dustIridiumChloride -dustTitaniumTrifluoride -dustCalciumPhosphide -dustIndiumPhosphide -dustBariumSulfide -dustTriniumSulfide -dustZincSulfide -dustGalliumSulfide -dustAntimonyTrifluoride -dustEnrichedNaquadahSulfate -dustNaquadriaSulfate -dustPyrochlore -dustRtmAlloy -dustSiliconeRubber -dustRawRubber -dustRawStyreneButadieneRubber -dustStyreneButadieneRubber -dustReinforcedEpoxyResin -dustPolyvinylChloride -dustPolyphenyleneSulfide -dustPolybenzimidazole -dustPolydimethylsiloxane -dustPlastic -dustEpoxy -dustPolycaprolactam -dustPolytetrafluoroethylene -dustRubber -dustCyclohexanoneOxime -dustCaprolactam -dustPolyvinylButyral -dustBiphenyl -dustOilsands -dustRareEarth -dustStone -dustNetherStar -dustNetherrack -dustCollagen -dustGelatin -dustAgar -dustMeat -dustPaper -dustLapotron -dustTreatedWood -dustGlass -dustPerlite -dustOlivine -dustOpal -dustAmethyst -dustApatite -dustBlackSteel -dustDamascusSteel -dustTungstenSteel -dustCobaltBrass -dustTricalciumPhosphate -dustGarnetRed -dustGarnetYellow -dustMarble -dustGraniteBlack -dustGraniteRed -dustVanadiumMagnetite -dustQuartzSand -dustPollucite -dustBentonite -dustFullersEarth -dustPitchblende -dustMonazite -dustMirabilite -dustTrona -dustGypsum -dustZeolite -dustConcrete -dustSteelMagnetic -dustVanadiumSteel -dustPotin -dustBorosilicateGlass -dustAndesite -dustNaquadahAlloy -dustFlint -dustPlatinumSludgeResidue -dustPalladiumRaw -dustRarestMetalMixture -dustAmmoniumChloride -dustRhodiumPlatedPalladium -dustClay -dustEnderEye -dustDiatomite -dustRedSteel -dustBlueSteel -dustBasalt -dustGraniticMineralSand -dustRedrock -dustGarnetSand -dustHssg -dustRedAlloy -dustBasalticMineralSand -dustHsse -dustHsss -dustIridiumMetalResidue -dustGranite -dustBrick -dustFireclay -dustDiorite -dustBlueAlloy -dustStellite100 -dustWatertightSteel -dustMaragingSteel300 -dustHastelloyC276 -dustHastelloyX -dustTrinaquadalloy -dustZeron100 -dustTitaniumCarbide -dustTantalumCarbide -dustMolybdenumDisilicide -dustHslaSteel -dustTitaniumTungstenCarbide -dustIncoloyMa956 -dustSmallDraconium -dustSmallAwakenedDraconium -dustSmallDarkSteel -dustSmallOmnium -dustSmallArdite -dustSmallManyullyn -dustSmallSignalum -dustSmallConductiveIron -dustSmallEnergeticAlloy -dustSmallVibrantAlloy -dustSmallPulsatingIron -dustSmallElectricalSteel -dustSmallLumium -dustSmallEnderium -dustSmallElectrumFlux -dustSmallMithril -dustSmallCrystalMatrix -dustSmallSoularium -dustSmallEndSteel -dustSmallInfinity -dustSmallMicroversium -dustSmallDraconicSuperconductor -dustSmallOsmiridium8020 -dustSmallIridosmine8020 -dustSmallKaemanite -dustSmallTungstenTrioxide -dustSmallBerylliumOxide -dustSmallNiobiumPentoxide -dustSmallTantalumPentoxide -dustSmallFluorite -dustSmallManganeseDifluoride -dustSmallMolybdenumTrioxide -dustSmallLeadChloride -dustSmallWollastonite -dustSmallSodiumMetavanadate -dustSmallVanadiumPentoxide -dustSmallAmmoniumMetavanadate -dustSmallPhthalicAnhydride -dustSmallEthylanthraquinone -dustSmallGrapheneOxide -dustSmallKaptonK -dustSmallDurene -dustSmallPyromelliticDianhydride -dustSmallOxydianiline -dustSmallNaquadahOxide -dustSmallPyromorphite -dustSmallNaquadahHydroxide -dustSmallSnowchestite -dustSmallCaesiumHydroxide -dustSmallLeadMetasilicate -dustSmallPlatinumMetallic -dustSmallPalladiumMetallic -dustSmallAmmoniumHexachloroplatinate -dustSmallPotassiumBisulfate -dustSmallPotassiumPyrosulfate -dustSmallPotassiumSulfate -dustSmallZincSulfate -dustSmallSodiumNitrate -dustSmallRhodiumNitrate -dustSmallSodiumRuthenate -dustSmallSodiumPeroxide -dustSmallIridiumDioxideResidue -dustSmallAmmoniumHexachloroiridiate -dustSmallPlatinumGroupResidue -dustSmallCrudePlatinumResidue -dustSmallCrudePalladiumResidue -dustSmallIridiumGroupSludge -dustSmallCrudeRhodiumResidue -dustSmallRhodiumSalt -dustSmallSodiumMethoxide -dustSmallStoneResidue -dustSmallUncommonResidue -dustSmallOxidisedResidue -dustSmallRefinedResidue -dustSmallCleanInertResidue -dustSmallTaranium -dustSmallDarmstadtite -dustSmallDulysite -dustSmallLaurite -dustSmallCuprorhodsite -dustSmallAluminium -dustSmallAmericium -dustSmallAntimony -dustSmallArsenic -dustSmallBarium -dustSmallBerkelium -dustSmallBeryllium -dustSmallBismuth -dustSmallBoron -dustSmallCaesium -dustSmallCalcium -dustSmallCalifornium -dustSmallCarbon -dustSmallCadmium -dustSmallCerium -dustSmallChrome -dustSmallCobalt -dustSmallCopper -dustSmallCurium -dustSmallDarmstadtium -dustSmallEinsteinium -dustSmallEuropium -dustSmallGallium -dustSmallGold -dustSmallIndium -dustSmallIridium -dustSmallIron -dustSmallLanthanum -dustSmallLead -dustSmallLithium -dustSmallLutetium -dustSmallMagnesium -dustSmallManganese -dustSmallMolybdenum -dustSmallNeodymium -dustSmallNeptunium -dustSmallNickel -dustSmallNiobium -dustSmallOsmium -dustSmallPalladium -dustSmallPhosphorus -dustSmallPlatinum -dustSmallPlutonium -dustSmallPlutonium239 -dustSmallPlutonium241 -dustSmallPotassium -dustSmallRhodium -dustSmallRuthenium -dustSmallSamarium -dustSmallSilicon -dustSmallSilver -dustSmallSodium -dustSmallSulfur -dustSmallTantalum -dustSmallThorium -dustSmallTin -dustSmallTitanium -dustSmallTungsten -dustSmallUranium -dustSmallUranium238 -dustSmallUranium235 -dustSmallVanadium -dustSmallYttrium -dustSmallZinc -dustSmallNaquadah -dustSmallNaquadahEnriched -dustSmallNaquadria -dustSmallNeutronium -dustSmallTritanium -dustSmallDuranium -dustSmallTrinium -dustSmallCertusQuartz -dustSmallAlmandine -dustSmallAndradite -dustSmallAnnealedCopper -dustSmallAsbestos -dustSmallAsh -dustSmallBandedIron -dustSmallBatteryAlloy -dustSmallBlueTopaz -dustSmallBone -dustSmallBrass -dustSmallBronze -dustSmallBrownLimonite -dustSmallCalcite -dustSmallCassiterite -dustSmallCassiteriteSand -dustSmallChalcopyrite -dustSmallCharcoal -dustSmallChromite -dustSmallCinnabar -dustSmallCoal -dustSmallCobaltite -dustSmallCooperite -dustSmallCupronickel -dustSmallDarkAsh -dustSmallDiamond -dustSmallElectrum -dustSmallEmerald -dustSmallGalena -dustSmallGarnierite -dustSmallGreenSapphire -dustSmallGrossular -dustSmallIce -dustSmallIlmenite -dustSmallRutile -dustSmallBauxite -dustSmallInvar -dustSmallKanthal -dustSmallLazurite -dustSmallMagnalium -dustSmallMagnesite -dustSmallMagnetite -dustSmallMolybdenite -dustSmallNichrome -dustSmallNiobiumNitride -dustSmallNiobiumTitanium -dustSmallObsidian -dustSmallPhosphate -dustSmallPlatinumRaw -dustSmallSterlingSilver -dustSmallRoseGold -dustSmallBlackBronze -dustSmallBismuthBronze -dustSmallBiotite -dustSmallPowellite -dustSmallPyrite -dustSmallPyrolusite -dustSmallPyrope -dustSmallRockSalt -dustSmallRuridit -dustSmallRuby -dustSmallSalt -dustSmallSaltpeter -dustSmallSapphire -dustSmallScheelite -dustSmallSodalite -dustSmallAluminiumSulfite -dustSmallTantalite -dustSmallCoke -dustSmallSolderingAlloy -dustSmallSpessartine -dustSmallSphalerite -dustSmallStainlessSteel -dustSmallSteel -dustSmallStibnite -dustSmallTetrahedrite -dustSmallTinAlloy -dustSmallTopaz -dustSmallTungstate -dustSmallUltimet -dustSmallUraninite -dustSmallUvarovite -dustSmallVanadiumGallium -dustSmallWroughtIron -dustSmallWulfenite -dustSmallYellowLimonite -dustSmallYttriumBariumCuprate -dustSmallNetherQuartz -dustSmallQuartzite -dustSmallGraphite -dustSmallGraphene -dustSmallTungsticAcid -dustSmallOsmiridium -dustSmallLithiumChloride -dustSmallCalciumChloride -dustSmallBornite -dustSmallChalcocite -dustSmallGalliumArsenide -dustSmallPotash -dustSmallSodaAsh -dustSmallIndiumGalliumPhosphide -dustSmallNickelZincFerrite -dustSmallSiliconDioxide -dustSmallMagnesiumChloride -dustSmallSodiumSulfide -dustSmallPhosphorusPentoxide -dustSmallQuicklime -dustSmallSodiumBisulfate -dustSmallFerriteMixture -dustSmallMagnesia -dustSmallPlatinumGroupSludge -dustSmallRealgar -dustSmallSodiumBicarbonate -dustSmallPotassiumDichromate -dustSmallChromiumTrioxide -dustSmallAntimonyTrioxide -dustSmallZincite -dustSmallCupricOxide -dustSmallCobaltOxide -dustSmallArsenicTrioxide -dustSmallMassicot -dustSmallFerrosilite -dustSmallMetalMixture -dustSmallSodiumHydroxide -dustSmallBastnasite -dustSmallPentlandite -dustSmallSpodumene -dustSmallLepidolite -dustSmallGlauconiteSand -dustSmallMalachite -dustSmallMica -dustSmallBarite -dustSmallAlunite -dustSmallTalc -dustSmallSoapstone -dustSmallKyanite -dustSmallIronMagnetic -dustSmallTungstenCarbide -dustSmallEnderPearl -dustSmallPotassiumFeldspar -dustSmallNeodymiumMagnetic -dustSmallSamariumMagnetic -dustSmallManganesePhosphide -dustSmallMagnesiumDiboride -dustSmallMercuryBariumCalciumCuprate -dustSmallUraniumTriplatinum -dustSmallSamariumIronArsenicOxide -dustSmallIndiumTinBariumTitaniumCuprate -dustSmallUraniumRhodiumDinaquadide -dustSmallEnrichedNaquadahTriniumEuropiumDuranide -dustSmallRutheniumTriniumAmericiumNeutronate -dustSmallInertMetalMixture -dustSmallRhodiumSulfate -dustSmallRutheniumTetroxide -dustSmallOsmiumTetroxide -dustSmallIridiumChloride -dustSmallTitaniumTrifluoride -dustSmallCalciumPhosphide -dustSmallIndiumPhosphide -dustSmallBariumSulfide -dustSmallTriniumSulfide -dustSmallZincSulfide -dustSmallGalliumSulfide -dustSmallAntimonyTrifluoride -dustSmallEnrichedNaquadahSulfate -dustSmallNaquadriaSulfate -dustSmallPyrochlore -dustSmallRtmAlloy -dustSmallSiliconeRubber -dustSmallRawRubber -dustSmallRawStyreneButadieneRubber -dustSmallStyreneButadieneRubber -dustSmallReinforcedEpoxyResin -dustSmallPolyvinylChloride -dustSmallPolyphenyleneSulfide -dustSmallPolybenzimidazole -dustSmallPolydimethylsiloxane -dustSmallPlastic -dustSmallEpoxy -dustSmallPolycaprolactam -dustSmallPolytetrafluoroethylene -dustSmallSugar -dustSmallRubber -dustSmallCyclohexanoneOxime -dustSmallCaprolactam -dustSmallPolyvinylButyral -dustSmallBiphenyl -dustSmallGunpowder -dustSmallOilsands -dustSmallRareEarth -dustSmallStone -dustSmallGlowstone -dustSmallNetherStar -dustSmallEndstone -dustSmallNetherrack -dustSmallCollagen -dustSmallGelatin -dustSmallAgar -dustSmallCocoa -dustSmallWheat -dustSmallMeat -dustSmallWood -dustSmallPaper -dustSmallTreatedWood -dustSmallGlass -dustSmallPerlite -dustSmallBorax -dustSmallOlivine -dustSmallOpal -dustSmallAmethyst -dustSmallLapis -dustSmallBlaze -dustSmallApatite -dustSmallBlackSteel -dustSmallDamascusSteel -dustSmallTungstenSteel -dustSmallCobaltBrass -dustSmallTricalciumPhosphate -dustSmallGarnetRed -dustSmallGarnetYellow -dustSmallMarble -dustSmallGraniteBlack -dustSmallGraniteRed -dustSmallVanadiumMagnetite -dustSmallQuartzSand -dustSmallPollucite -dustSmallBentonite -dustSmallFullersEarth -dustSmallPitchblende -dustSmallMonazite -dustSmallMirabilite -dustSmallTrona -dustSmallGypsum -dustSmallZeolite -dustSmallConcrete -dustSmallSteelMagnetic -dustSmallVanadiumSteel -dustSmallPotin -dustSmallBorosilicateGlass -dustSmallAndesite -dustSmallNaquadahAlloy -dustSmallFlint -dustSmallPlatinumSludgeResidue -dustSmallPalladiumRaw -dustSmallRarestMetalMixture -dustSmallAmmoniumChloride -dustSmallRhodiumPlatedPalladium -dustSmallClay -dustSmallRedstone -dustSmallElectrotine -dustSmallEnderEye -dustSmallDiatomite -dustSmallRedSteel -dustSmallBlueSteel -dustSmallBasalt -dustSmallGraniticMineralSand -dustSmallRedrock -dustSmallGarnetSand -dustSmallHssg -dustSmallRedAlloy -dustSmallBasalticMineralSand -dustSmallHsse -dustSmallHsss -dustSmallIridiumMetalResidue -dustSmallGranite -dustSmallBrick -dustSmallFireclay -dustSmallDiorite -dustSmallBlueAlloy -dustSmallStellite100 -dustSmallWatertightSteel -dustSmallMaragingSteel300 -dustSmallHastelloyC276 -dustSmallHastelloyX -dustSmallTrinaquadalloy -dustSmallZeron100 -dustSmallTitaniumCarbide -dustSmallTantalumCarbide -dustSmallMolybdenumDisilicide -dustSmallHslaSteel -dustSmallTitaniumTungstenCarbide -dustSmallIncoloyMa956 -dustTinyDraconium -dustTinyAwakenedDraconium -dustTinyDarkSteel -dustTinyOmnium -dustTinyArdite -dustTinyManyullyn -dustTinySignalum -dustTinyConductiveIron -dustTinyEnergeticAlloy -dustTinyVibrantAlloy -dustTinyPulsatingIron -dustTinyElectricalSteel -dustTinyLumium -dustTinyEnderium -dustTinyElectrumFlux -dustTinyMithril -dustTinyCrystalMatrix -dustTinySoularium -dustTinyEndSteel -dustTinyInfinity -dustTinyMicroversium -dustTinyDraconicSuperconductor -dustTinyOsmiridium8020 -dustTinyIridosmine8020 -dustTinyKaemanite -dustTinyTungstenTrioxide -dustTinyBerylliumOxide -dustTinyNiobiumPentoxide -dustTinyTantalumPentoxide -dustTinyFluorite -dustTinyManganeseDifluoride -dustTinyMolybdenumTrioxide -dustTinyLeadChloride -dustTinyWollastonite -dustTinySodiumMetavanadate -dustTinyVanadiumPentoxide -dustTinyAmmoniumMetavanadate -dustTinyPhthalicAnhydride -dustTinyEthylanthraquinone -dustTinyGrapheneOxide -dustTinyKaptonK -dustTinyDurene -dustTinyPyromelliticDianhydride -dustTinyOxydianiline -dustTinyNaquadahOxide -dustTinyPyromorphite -dustTinyNaquadahHydroxide -dustTinySnowchestite -dustTinyCaesiumHydroxide -dustTinyLeadMetasilicate -dustTinyPlatinumMetallic -dustTinyPalladiumMetallic -dustTinyAmmoniumHexachloroplatinate -dustTinyPotassiumBisulfate -dustTinyPotassiumPyrosulfate -dustTinyPotassiumSulfate -dustTinyZincSulfate -dustTinySodiumNitrate -dustTinyRhodiumNitrate -dustTinySodiumRuthenate -dustTinySodiumPeroxide -dustTinyIridiumDioxideResidue -dustTinyAmmoniumHexachloroiridiate -dustTinyPlatinumGroupResidue -dustTinyCrudePlatinumResidue -dustTinyCrudePalladiumResidue -dustTinyIridiumGroupSludge -dustTinyCrudeRhodiumResidue -dustTinyRhodiumSalt -dustTinySodiumMethoxide -dustTinyStoneResidue -dustTinyUncommonResidue -dustTinyOxidisedResidue -dustTinyRefinedResidue -dustTinyCleanInertResidue -dustTinyTaranium -dustTinyDarmstadtite -dustTinyDulysite -dustTinyLaurite -dustTinyCuprorhodsite -dustTinyAluminium -dustTinyAmericium -dustTinyAntimony -dustTinyArsenic -dustTinyBarium -dustTinyBerkelium -dustTinyBeryllium -dustTinyBismuth -dustTinyBoron -dustTinyCaesium -dustTinyCalcium -dustTinyCalifornium -dustTinyCarbon -dustTinyCadmium -dustTinyCerium -dustTinyChrome -dustTinyCobalt -dustTinyCopper -dustTinyCurium -dustTinyDarmstadtium -dustTinyEinsteinium -dustTinyEuropium -dustTinyGallium -dustTinyGold -dustTinyIndium -dustTinyIridium -dustTinyIron -dustTinyLanthanum -dustTinyLead -dustTinyLithium -dustTinyLutetium -dustTinyMagnesium -dustTinyManganese -dustTinyMolybdenum -dustTinyNeodymium -dustTinyNeptunium -dustTinyNickel -dustTinyNiobium -dustTinyOsmium -dustTinyPalladium -dustTinyPhosphorus -dustTinyPlatinum -dustTinyPlutonium -dustTinyPlutonium239 -dustTinyPlutonium241 -dustTinyPotassium -dustTinyRhodium -dustTinyRuthenium -dustTinySamarium -dustTinySilicon -dustTinySilver -dustTinySodium -dustTinySulfur -dustTinyTantalum -dustTinyThorium -dustTinyTin -dustTinyTitanium -dustTinyTungsten -dustTinyUranium -dustTinyUranium238 -dustTinyUranium235 -dustTinyVanadium -dustTinyYttrium -dustTinyZinc -dustTinyNaquadah -dustTinyNaquadahEnriched -dustTinyNaquadria -dustTinyNeutronium -dustTinyTritanium -dustTinyDuranium -dustTinyTrinium -dustTinyCertusQuartz -dustTinyAlmandine -dustTinyAndradite -dustTinyAnnealedCopper -dustTinyAsbestos -dustTinyAsh -dustTinyBandedIron -dustTinyBatteryAlloy -dustTinyBlueTopaz -dustTinyBone -dustTinyBrass -dustTinyBronze -dustTinyBrownLimonite -dustTinyCalcite -dustTinyCassiterite -dustTinyCassiteriteSand -dustTinyChalcopyrite -dustTinyCharcoal -dustTinyChromite -dustTinyCinnabar -dustTinyCoal -dustTinyCobaltite -dustTinyCooperite -dustTinyCupronickel -dustTinyDarkAsh -dustTinyDiamond -dustTinyElectrum -dustTinyEmerald -dustTinyGalena -dustTinyGarnierite -dustTinyGreenSapphire -dustTinyGrossular -dustTinyIce -dustTinyIlmenite -dustTinyRutile -dustTinyBauxite -dustTinyInvar -dustTinyKanthal -dustTinyLazurite -dustTinyMagnalium -dustTinyMagnesite -dustTinyMagnetite -dustTinyMolybdenite -dustTinyNichrome -dustTinyNiobiumNitride -dustTinyNiobiumTitanium -dustTinyObsidian -dustTinyPhosphate -dustTinyPlatinumRaw -dustTinySterlingSilver -dustTinyRoseGold -dustTinyBlackBronze -dustTinyBismuthBronze -dustTinyBiotite -dustTinyPowellite -dustTinyPyrite -dustTinyPyrolusite -dustTinyPyrope -dustTinyRockSalt -dustTinyRuridit -dustTinyRuby -dustTinySalt -dustTinySaltpeter -dustTinySapphire -dustTinyScheelite -dustTinySodalite -dustTinyAluminiumSulfite -dustTinyTantalite -dustTinyCoke -dustTinySolderingAlloy -dustTinySpessartine -dustTinySphalerite -dustTinyStainlessSteel -dustTinySteel -dustTinyStibnite -dustTinyTetrahedrite -dustTinyTinAlloy -dustTinyTopaz -dustTinyTungstate -dustTinyUltimet -dustTinyUraninite -dustTinyUvarovite -dustTinyVanadiumGallium -dustTinyWroughtIron -dustTinyWulfenite -dustTinyYellowLimonite -dustTinyYttriumBariumCuprate -dustTinyNetherQuartz -dustTinyQuartzite -dustTinyGraphite -dustTinyGraphene -dustTinyTungsticAcid -dustTinyOsmiridium -dustTinyLithiumChloride -dustTinyCalciumChloride -dustTinyBornite -dustTinyChalcocite -dustTinyGalliumArsenide -dustTinyPotash -dustTinySodaAsh -dustTinyIndiumGalliumPhosphide -dustTinyNickelZincFerrite -dustTinySiliconDioxide -dustTinyMagnesiumChloride -dustTinySodiumSulfide -dustTinyPhosphorusPentoxide -dustTinyQuicklime -dustTinySodiumBisulfate -dustTinyFerriteMixture -dustTinyMagnesia -dustTinyPlatinumGroupSludge -dustTinyRealgar -dustTinySodiumBicarbonate -dustTinyPotassiumDichromate -dustTinyChromiumTrioxide -dustTinyAntimonyTrioxide -dustTinyZincite -dustTinyCupricOxide -dustTinyCobaltOxide -dustTinyArsenicTrioxide -dustTinyMassicot -dustTinyFerrosilite -dustTinyMetalMixture -dustTinySodiumHydroxide -dustTinyBastnasite -dustTinyPentlandite -dustTinySpodumene -dustTinyLepidolite -dustTinyGlauconiteSand -dustTinyMalachite -dustTinyMica -dustTinyBarite -dustTinyAlunite -dustTinyTalc -dustTinySoapstone -dustTinyKyanite -dustTinyIronMagnetic -dustTinyTungstenCarbide -dustTinyEnderPearl -dustTinyPotassiumFeldspar -dustTinyNeodymiumMagnetic -dustTinySamariumMagnetic -dustTinyManganesePhosphide -dustTinyMagnesiumDiboride -dustTinyMercuryBariumCalciumCuprate -dustTinyUraniumTriplatinum -dustTinySamariumIronArsenicOxide -dustTinyIndiumTinBariumTitaniumCuprate -dustTinyUraniumRhodiumDinaquadide -dustTinyEnrichedNaquadahTriniumEuropiumDuranide -dustTinyRutheniumTriniumAmericiumNeutronate -dustTinyInertMetalMixture -dustTinyRhodiumSulfate -dustTinyRutheniumTetroxide -dustTinyOsmiumTetroxide -dustTinyIridiumChloride -dustTinyTitaniumTrifluoride -dustTinyCalciumPhosphide -dustTinyIndiumPhosphide -dustTinyBariumSulfide -dustTinyTriniumSulfide -dustTinyZincSulfide -dustTinyGalliumSulfide -dustTinyAntimonyTrifluoride -dustTinyEnrichedNaquadahSulfate -dustTinyNaquadriaSulfate -dustTinyPyrochlore -dustTinyRtmAlloy -dustTinySiliconeRubber -dustTinyRawRubber -dustTinyRawStyreneButadieneRubber -dustTinyStyreneButadieneRubber -dustTinyReinforcedEpoxyResin -dustTinyPolyvinylChloride -dustTinyPolyphenyleneSulfide -dustTinyPolybenzimidazole -dustTinyPolydimethylsiloxane -dustTinyPlastic -dustTinyEpoxy -dustTinyPolycaprolactam -dustTinyPolytetrafluoroethylene -dustTinySugar -dustTinyRubber -dustTinyCyclohexanoneOxime -dustTinyCaprolactam -dustTinyPolyvinylButyral -dustTinyBiphenyl -dustTinyGunpowder -dustTinyOilsands -dustTinyRareEarth -dustTinyStone -dustTinyGlowstone -dustTinyNetherStar -dustTinyEndstone -dustTinyNetherrack -dustTinyCollagen -dustTinyGelatin -dustTinyAgar -dustTinyCocoa -dustTinyWheat -dustTinyMeat -dustTinyWood -dustTinyPaper -dustTinyTreatedWood -dustTinyGlass -dustTinyPerlite -dustTinyBorax -dustTinyOlivine -dustTinyOpal -dustTinyAmethyst -dustTinyLapis -dustTinyBlaze -dustTinyApatite -dustTinyBlackSteel -dustTinyDamascusSteel -dustTinyTungstenSteel -dustTinyCobaltBrass -dustTinyTricalciumPhosphate -dustTinyGarnetRed -dustTinyGarnetYellow -dustTinyMarble -dustTinyGraniteBlack -dustTinyGraniteRed -dustTinyVanadiumMagnetite -dustTinyQuartzSand -dustTinyPollucite -dustTinyBentonite -dustTinyFullersEarth -dustTinyPitchblende -dustTinyMonazite -dustTinyMirabilite -dustTinyTrona -dustTinyGypsum -dustTinyZeolite -dustTinyConcrete -dustTinySteelMagnetic -dustTinyVanadiumSteel -dustTinyPotin -dustTinyBorosilicateGlass -dustTinyAndesite -dustTinyNaquadahAlloy -dustTinyFlint -dustTinyPlatinumSludgeResidue -dustTinyPalladiumRaw -dustTinyRarestMetalMixture -dustTinyAmmoniumChloride -dustTinyRhodiumPlatedPalladium -dustTinyClay -dustTinyRedstone -dustTinyElectrotine -dustTinyEnderEye -dustTinyDiatomite -dustTinyRedSteel -dustTinyBlueSteel -dustTinyBasalt -dustTinyGraniticMineralSand -dustTinyRedrock -dustTinyGarnetSand -dustTinyHssg -dustTinyRedAlloy -dustTinyBasalticMineralSand -dustTinyHsse -dustTinyHsss -dustTinyIridiumMetalResidue -dustTinyGranite -dustTinyBrick -dustTinyFireclay -dustTinyDiorite -dustTinyBlueAlloy -dustTinyStellite100 -dustTinyWatertightSteel -dustTinyMaragingSteel300 -dustTinyHastelloyC276 -dustTinyHastelloyX -dustTinyTrinaquadalloy -dustTinyZeron100 -dustTinyTitaniumCarbide -dustTinyTantalumCarbide -dustTinyMolybdenumDisilicide -dustTinyHslaSteel -dustTinyTitaniumTungstenCarbide -dustTinyIncoloyMa956 -dustImpureDraconium -dustImpureOsmiridium8020 -dustImpureIridosmine8020 -dustImpureKaemanite -dustImpureFluorite -dustImpureSnowchestite -dustImpureDarmstadtite -dustImpureDulysite -dustImpureLaurite -dustImpureCuprorhodsite -dustImpureAluminium -dustImpureBeryllium -dustImpureCobalt -dustImpureCopper -dustImpureGold -dustImpureIron -dustImpureLead -dustImpureLithium -dustImpureMolybdenum -dustImpureNeodymium -dustImpureNickel -dustImpurePalladium -dustImpurePlatinum -dustImpurePlutonium -dustImpurePlutonium239 -dustImpureSilver -dustImpureSulfur -dustImpureThorium -dustImpureTin -dustImpureNaquadah -dustImpureCertusQuartz -dustImpureAlmandine -dustImpureAsbestos -dustImpureBandedIron -dustImpureBlueTopaz -dustImpureBrownLimonite -dustImpureCalcite -dustImpureCassiterite -dustImpureCassiteriteSand -dustImpureChalcopyrite -dustImpureChromite -dustImpureCinnabar -dustImpureCoal -dustImpureCobaltite -dustImpureCooperite -dustImpureDiamond -dustImpureEmerald -dustImpureGalena -dustImpureGarnierite -dustImpureGreenSapphire -dustImpureGrossular -dustImpureIlmenite -dustImpureBauxite -dustImpureLazurite -dustImpureMagnesite -dustImpureMagnetite -dustImpureMolybdenite -dustImpurePowellite -dustImpurePyrite -dustImpurePyrolusite -dustImpurePyrope -dustImpureRockSalt -dustImpureRuby -dustImpureSalt -dustImpureSaltpeter -dustImpureSapphire -dustImpureScheelite -dustImpureSodalite -dustImpureTantalite -dustImpureSpessartine -dustImpureSphalerite -dustImpureStibnite -dustImpureTetrahedrite -dustImpureTopaz -dustImpureTungstate -dustImpureUraninite -dustImpureWulfenite -dustImpureYellowLimonite -dustImpureNetherQuartz -dustImpureQuartzite -dustImpureGraphite -dustImpureBornite -dustImpureChalcocite -dustImpureRealgar -dustImpureBastnasite -dustImpurePentlandite -dustImpureSpodumene -dustImpureLepidolite -dustImpureGlauconiteSand -dustImpureMalachite -dustImpureMica -dustImpureBarite -dustImpureAlunite -dustImpureTalc -dustImpureSoapstone -dustImpureKyanite -dustImpurePyrochlore -dustImpureOilsands -dustImpureOlivine -dustImpureOpal -dustImpureAmethyst -dustImpureLapis -dustImpureApatite -dustImpureTricalciumPhosphate -dustImpureGarnetRed -dustImpureGarnetYellow -dustImpureVanadiumMagnetite -dustImpurePollucite -dustImpureBentonite -dustImpureFullersEarth -dustImpurePitchblende -dustImpureMonazite -dustImpureTrona -dustImpureGypsum -dustImpureZeolite -dustImpureRedstone -dustImpureElectrotine -dustImpureDiatomite -dustImpureGraniticMineralSand -dustImpureGarnetSand -dustImpureBasalticMineralSand -dustPureDraconium -dustPureOsmiridium8020 -dustPureIridosmine8020 -dustPureKaemanite -dustPureFluorite -dustPureSnowchestite -dustPureDarmstadtite -dustPureDulysite -dustPureLaurite -dustPureCuprorhodsite -dustPureAluminium -dustPureBeryllium -dustPureCobalt -dustPureCopper -dustPureGold -dustPureIron -dustPureLead -dustPureLithium -dustPureMolybdenum -dustPureNeodymium -dustPureNickel -dustPurePalladium -dustPurePlatinum -dustPurePlutonium -dustPurePlutonium239 -dustPureSilver -dustPureSulfur -dustPureThorium -dustPureTin -dustPureNaquadah -dustPureCertusQuartz -dustPureAlmandine -dustPureAsbestos -dustPureBandedIron -dustPureBlueTopaz -dustPureBrownLimonite -dustPureCalcite -dustPureCassiterite -dustPureCassiteriteSand -dustPureChalcopyrite -dustPureChromite -dustPureCinnabar -dustPureCoal -dustPureCobaltite -dustPureCooperite -dustPureDiamond -dustPureEmerald -dustPureGalena -dustPureGarnierite -dustPureGreenSapphire -dustPureGrossular -dustPureIlmenite -dustPureBauxite -dustPureLazurite -dustPureMagnesite -dustPureMagnetite -dustPureMolybdenite -dustPurePowellite -dustPurePyrite -dustPurePyrolusite -dustPurePyrope -dustPureRockSalt -dustPureRuby -dustPureSalt -dustPureSaltpeter -dustPureSapphire -dustPureScheelite -dustPureSodalite -dustPureTantalite -dustPureSpessartine -dustPureSphalerite -dustPureStibnite -dustPureTetrahedrite -dustPureTopaz -dustPureTungstate -dustPureUraninite -dustPureWulfenite -dustPureYellowLimonite -dustPureNetherQuartz -dustPureQuartzite -dustPureGraphite -dustPureBornite -dustPureChalcocite -dustPureRealgar -dustPureBastnasite -dustPurePentlandite -dustPureSpodumene -dustPureLepidolite -dustPureGlauconiteSand -dustPureMalachite -dustPureMica -dustPureBarite -dustPureAlunite -dustPureTalc -dustPureSoapstone -dustPureKyanite -dustPurePyrochlore -dustPureOilsands -dustPureOlivine -dustPureOpal -dustPureAmethyst -dustPureLapis -dustPureApatite -dustPureTricalciumPhosphate -dustPureGarnetRed -dustPureGarnetYellow -dustPureVanadiumMagnetite -dustPurePollucite -dustPureBentonite -dustPureFullersEarth -dustPurePitchblende -dustPureMonazite -dustPureTrona -dustPureGypsum -dustPureZeolite -dustPureRedstone -dustPureElectrotine -dustPureDiatomite -dustPureGraniticMineralSand -dustPureGarnetSand -dustPureBasalticMineralSand -crushedDraconium -crushedOsmiridium8020 -crushedIridosmine8020 -crushedKaemanite -crushedFluorite -crushedSnowchestite -crushedDarmstadtite -crushedDulysite -crushedLaurite -crushedCuprorhodsite -crushedAluminium -crushedBeryllium -crushedCobalt -crushedCopper -crushedGold -crushedIron -crushedLead -crushedLithium -crushedMolybdenum -crushedNeodymium -crushedNickel -crushedPalladium -crushedPlatinum -crushedPlutonium -crushedPlutonium239 -crushedSilver -crushedSulfur -crushedThorium -crushedTin -crushedNaquadah -crushedCertusQuartz -crushedAlmandine -crushedAsbestos -crushedBandedIron -crushedBlueTopaz -crushedBrownLimonite -crushedCalcite -crushedCassiterite -crushedCassiteriteSand -crushedChalcopyrite -crushedChromite -crushedCinnabar -crushedCoal -crushedCobaltite -crushedCooperite -crushedDiamond -crushedEmerald -crushedGalena -crushedGarnierite -crushedGreenSapphire -crushedGrossular -crushedIlmenite -crushedBauxite -crushedLazurite -crushedMagnesite -crushedMagnetite -crushedMolybdenite -crushedPowellite -crushedPyrite -crushedPyrolusite -crushedPyrope -crushedRockSalt -crushedRuby -crushedSalt -crushedSaltpeter -crushedSapphire -crushedScheelite -crushedSodalite -crushedTantalite -crushedSpessartine -crushedSphalerite -crushedStibnite -crushedTetrahedrite -crushedTopaz -crushedTungstate -crushedUraninite -crushedWulfenite -crushedYellowLimonite -crushedNetherQuartz -crushedQuartzite -crushedGraphite -crushedBornite -crushedChalcocite -crushedRealgar -crushedBastnasite -crushedPentlandite -crushedSpodumene -crushedLepidolite -crushedGlauconiteSand -crushedMalachite -crushedMica -crushedBarite -crushedAlunite -crushedTalc -crushedSoapstone -crushedKyanite -crushedPyrochlore -crushedOilsands -crushedOlivine -crushedOpal -crushedAmethyst -crushedLapis -crushedApatite -crushedTricalciumPhosphate -crushedGarnetRed -crushedGarnetYellow -crushedVanadiumMagnetite -crushedPollucite -crushedBentonite -crushedFullersEarth -crushedPitchblende -crushedMonazite -crushedTrona -crushedGypsum -crushedZeolite -crushedRedstone -crushedElectrotine -crushedDiatomite -crushedGraniticMineralSand -crushedGarnetSand -crushedBasalticMineralSand -crushedPurifiedDraconium -crushedPurifiedOsmiridium8020 -crushedPurifiedIridosmine8020 -crushedPurifiedKaemanite -crushedPurifiedFluorite -crushedPurifiedSnowchestite -crushedPurifiedDarmstadtite -crushedPurifiedDulysite -crushedPurifiedLaurite -crushedPurifiedCuprorhodsite -crushedPurifiedAluminium -crushedPurifiedBeryllium -crushedPurifiedCobalt -crushedPurifiedCopper -crushedPurifiedGold -crushedPurifiedIron -crushedPurifiedLead -crushedPurifiedLithium -crushedPurifiedMolybdenum -crushedPurifiedNeodymium -crushedPurifiedNickel -crushedPurifiedPalladium -crushedPurifiedPlatinum -crushedPurifiedPlutonium -crushedPurifiedPlutonium239 -crushedPurifiedSilver -crushedPurifiedSulfur -crushedPurifiedThorium -crushedPurifiedTin -crushedPurifiedNaquadah -crushedPurifiedCertusQuartz -crushedPurifiedAlmandine -crushedPurifiedAsbestos -crushedPurifiedBandedIron -crushedPurifiedBlueTopaz -crushedPurifiedBrownLimonite -crushedPurifiedCalcite -crushedPurifiedCassiterite -crushedPurifiedCassiteriteSand -crushedPurifiedChalcopyrite -crushedPurifiedChromite -crushedPurifiedCinnabar -crushedPurifiedCoal -crushedPurifiedCobaltite -crushedPurifiedCooperite -crushedPurifiedDiamond -crushedPurifiedEmerald -crushedPurifiedGalena -crushedPurifiedGarnierite -crushedPurifiedGreenSapphire -crushedPurifiedGrossular -crushedPurifiedIlmenite -crushedPurifiedBauxite -crushedPurifiedLazurite -crushedPurifiedMagnesite -crushedPurifiedMagnetite -crushedPurifiedMolybdenite -crushedPurifiedPowellite -crushedPurifiedPyrite -crushedPurifiedPyrolusite -crushedPurifiedPyrope -crushedPurifiedRockSalt -crushedPurifiedRuby -crushedPurifiedSalt -crushedPurifiedSaltpeter -crushedPurifiedSapphire -crushedPurifiedScheelite -crushedPurifiedSodalite -crushedPurifiedTantalite -crushedPurifiedSpessartine -crushedPurifiedSphalerite -crushedPurifiedStibnite -crushedPurifiedTetrahedrite -crushedPurifiedTopaz -crushedPurifiedTungstate -crushedPurifiedUraninite -crushedPurifiedWulfenite -crushedPurifiedYellowLimonite -crushedPurifiedNetherQuartz -crushedPurifiedQuartzite -crushedPurifiedGraphite -crushedPurifiedBornite -crushedPurifiedChalcocite -crushedPurifiedRealgar -crushedPurifiedBastnasite -crushedPurifiedPentlandite -crushedPurifiedSpodumene -crushedPurifiedLepidolite -crushedPurifiedGlauconiteSand -crushedPurifiedMalachite -crushedPurifiedMica -crushedPurifiedBarite -crushedPurifiedAlunite -crushedPurifiedTalc -crushedPurifiedSoapstone -crushedPurifiedKyanite -crushedPurifiedPyrochlore -crushedPurifiedOilsands -crushedPurifiedOlivine -crushedPurifiedOpal -crushedPurifiedAmethyst -crushedPurifiedLapis -crushedPurifiedApatite -crushedPurifiedTricalciumPhosphate -crushedPurifiedGarnetRed -crushedPurifiedGarnetYellow -crushedPurifiedVanadiumMagnetite -crushedPurifiedPollucite -crushedPurifiedBentonite -crushedPurifiedFullersEarth -crushedPurifiedPitchblende -crushedPurifiedMonazite -crushedPurifiedTrona -crushedPurifiedGypsum -crushedPurifiedZeolite -crushedPurifiedRedstone -crushedPurifiedElectrotine -crushedPurifiedDiatomite -crushedPurifiedGraniticMineralSand -crushedPurifiedGarnetSand -crushedPurifiedBasalticMineralSand -crushedCentrifugedDraconium -crushedCentrifugedOsmiridium8020 -crushedCentrifugedIridosmine8020 -crushedCentrifugedKaemanite -crushedCentrifugedFluorite -crushedCentrifugedSnowchestite -crushedCentrifugedDarmstadtite -crushedCentrifugedDulysite -crushedCentrifugedLaurite -crushedCentrifugedCuprorhodsite -crushedCentrifugedAluminium -crushedCentrifugedBeryllium -crushedCentrifugedCobalt -crushedCentrifugedCopper -crushedCentrifugedGold -crushedCentrifugedIron -crushedCentrifugedLead -crushedCentrifugedLithium -crushedCentrifugedMolybdenum -crushedCentrifugedNeodymium -crushedCentrifugedNickel -crushedCentrifugedPalladium -crushedCentrifugedPlatinum -crushedCentrifugedPlutonium -crushedCentrifugedPlutonium239 -crushedCentrifugedSilver -crushedCentrifugedSulfur -crushedCentrifugedThorium -crushedCentrifugedTin -crushedCentrifugedNaquadah -crushedCentrifugedCertusQuartz -crushedCentrifugedAlmandine -crushedCentrifugedAsbestos -crushedCentrifugedBandedIron -crushedCentrifugedBlueTopaz -crushedCentrifugedBrownLimonite -crushedCentrifugedCalcite -crushedCentrifugedCassiterite -crushedCentrifugedCassiteriteSand -crushedCentrifugedChalcopyrite -crushedCentrifugedChromite -crushedCentrifugedCinnabar -crushedCentrifugedCoal -crushedCentrifugedCobaltite -crushedCentrifugedCooperite -crushedCentrifugedDiamond -crushedCentrifugedEmerald -crushedCentrifugedGalena -crushedCentrifugedGarnierite -crushedCentrifugedGreenSapphire -crushedCentrifugedGrossular -crushedCentrifugedIlmenite -crushedCentrifugedBauxite -crushedCentrifugedLazurite -crushedCentrifugedMagnesite -crushedCentrifugedMagnetite -crushedCentrifugedMolybdenite -crushedCentrifugedPowellite -crushedCentrifugedPyrite -crushedCentrifugedPyrolusite -crushedCentrifugedPyrope -crushedCentrifugedRockSalt -crushedCentrifugedRuby -crushedCentrifugedSalt -crushedCentrifugedSaltpeter -crushedCentrifugedSapphire -crushedCentrifugedScheelite -crushedCentrifugedSodalite -crushedCentrifugedTantalite -crushedCentrifugedSpessartine -crushedCentrifugedSphalerite -crushedCentrifugedStibnite -crushedCentrifugedTetrahedrite -crushedCentrifugedTopaz -crushedCentrifugedTungstate -crushedCentrifugedUraninite -crushedCentrifugedWulfenite -crushedCentrifugedYellowLimonite -crushedCentrifugedNetherQuartz -crushedCentrifugedQuartzite -crushedCentrifugedGraphite -crushedCentrifugedBornite -crushedCentrifugedChalcocite -crushedCentrifugedRealgar -crushedCentrifugedBastnasite -crushedCentrifugedPentlandite -crushedCentrifugedSpodumene -crushedCentrifugedLepidolite -crushedCentrifugedGlauconiteSand -crushedCentrifugedMalachite -crushedCentrifugedMica -crushedCentrifugedBarite -crushedCentrifugedAlunite -crushedCentrifugedTalc -crushedCentrifugedSoapstone -crushedCentrifugedKyanite -crushedCentrifugedPyrochlore -crushedCentrifugedOilsands -crushedCentrifugedOlivine -crushedCentrifugedOpal -crushedCentrifugedAmethyst -crushedCentrifugedLapis -crushedCentrifugedApatite -crushedCentrifugedTricalciumPhosphate -crushedCentrifugedGarnetRed -crushedCentrifugedGarnetYellow -crushedCentrifugedVanadiumMagnetite -crushedCentrifugedPollucite -crushedCentrifugedBentonite -crushedCentrifugedFullersEarth -crushedCentrifugedPitchblende -crushedCentrifugedMonazite -crushedCentrifugedTrona -crushedCentrifugedGypsum -crushedCentrifugedZeolite -crushedCentrifugedRedstone -crushedCentrifugedElectrotine -crushedCentrifugedDiatomite -crushedCentrifugedGraniticMineralSand -crushedCentrifugedGarnetSand -crushedCentrifugedBasalticMineralSand -gemDulysite -gemLaurite -gemCertusQuartz -gemAlmandine -gemAndradite -gemBlueTopaz -gemCinnabar -gemGreenSapphire -gemGrossular -gemRutile -gemLazurite -gemPyrope -gemRockSalt -gemSalt -gemSodalite -gemCoke -gemSpessartine -gemTopaz -gemUvarovite -gemQuartzite -gemRealgar -gemMalachite -gemSugar -gemLapotron -gemGlass -gemOlivine -gemOpal -gemApatite -gemGarnetRed -gemGarnetYellow -gemMonazite -gemFlawlessDulysite -gemFlawlessLaurite -gemFlawlessCertusQuartz -gemFlawlessAlmandine -gemFlawlessAndradite -gemFlawlessBlueTopaz -gemFlawlessCinnabar -gemFlawlessCoal -gemFlawlessDiamond -gemFlawlessEmerald -gemFlawlessGreenSapphire -gemFlawlessGrossular -gemFlawlessRutile -gemFlawlessLazurite -gemFlawlessPyrope -gemFlawlessRockSalt -gemFlawlessRuby -gemFlawlessSalt -gemFlawlessSapphire -gemFlawlessSodalite -gemFlawlessCoke -gemFlawlessSpessartine -gemFlawlessTopaz -gemFlawlessUvarovite -gemFlawlessNetherQuartz -gemFlawlessQuartzite -gemFlawlessRealgar -gemFlawlessMalachite -gemFlawlessGlass -gemFlawlessOlivine -gemFlawlessOpal -gemFlawlessAmethyst -gemFlawlessLapis -gemFlawlessApatite -gemFlawlessGarnetRed -gemFlawlessGarnetYellow -gemFlawlessMonazite -gemExquisiteDulysite -gemExquisiteLaurite -gemExquisiteCertusQuartz -gemExquisiteAlmandine -gemExquisiteAndradite -gemExquisiteBlueTopaz -gemExquisiteCinnabar -gemExquisiteCoal -gemExquisiteDiamond -gemExquisiteEmerald -gemExquisiteGreenSapphire -gemExquisiteGrossular -gemExquisiteRutile -gemExquisiteLazurite -gemExquisitePyrope -gemExquisiteRockSalt -gemExquisiteRuby -gemExquisiteSalt -gemExquisiteSapphire -gemExquisiteSodalite -gemExquisiteCoke -gemExquisiteSpessartine -gemExquisiteTopaz -gemExquisiteUvarovite -gemExquisiteNetherQuartz -gemExquisiteQuartzite -gemExquisiteRealgar -gemExquisiteMalachite -gemExquisiteGlass -gemExquisiteOlivine -gemExquisiteOpal -gemExquisiteAmethyst -gemExquisiteLapis -gemExquisiteApatite -gemExquisiteGarnetRed -gemExquisiteGarnetYellow -gemExquisiteMonazite -ingotAwakenedDraconium -ingotOmnium -ingotMicroversium -ingotDraconicSuperconductor -ingotBerylliumOxide -ingotKaptonK -ingotTaranium -ingotAmericium -ingotAntimony -ingotBerkelium -ingotBismuth -ingotCalifornium -ingotChrome -ingotCurium -ingotDarmstadtium -ingotEinsteinium -ingotEuropium -ingotGallium -ingotIndium -ingotLutetium -ingotMolybdenum -ingotNeodymium -ingotNeptunium -ingotNiobium -ingotPalladium -ingotPlutonium -ingotRhodium -ingotRuthenium -ingotSamarium -ingotTantalum -ingotThorium -ingotTungsten -ingotUranium -ingotVanadium -ingotYttrium -ingotZinc -ingotNaquadah -ingotNaquadahEnriched -ingotNaquadria -ingotNeutronium -ingotTritanium -ingotDuranium -ingotTrinium -ingotAnnealedCopper -ingotBatteryAlloy -ingotCupronickel -ingotKanthal -ingotMagnalium -ingotNichrome -ingotNiobiumNitride -ingotNiobiumTitanium -ingotSterlingSilver -ingotRoseGold -ingotBlackBronze -ingotBismuthBronze -ingotRuridit -ingotSolderingAlloy -ingotStainlessSteel -ingotTinAlloy -ingotUltimet -ingotVanadiumGallium -ingotWroughtIron -ingotYttriumBariumCuprate -ingotGalliumArsenide -ingotIndiumGalliumPhosphide -ingotNickelZincFerrite -ingotIronMagnetic -ingotTungstenCarbide -ingotNeodymiumMagnetic -ingotSamariumMagnetic -ingotManganesePhosphide -ingotMercuryBariumCalciumCuprate -ingotUraniumTriplatinum -ingotSamariumIronArsenicOxide -ingotIndiumTinBariumTitaniumCuprate -ingotUraniumRhodiumDinaquadide -ingotEnrichedNaquadahTriniumEuropiumDuranide -ingotRutheniumTriniumAmericiumNeutronate -ingotRtmAlloy -ingotSiliconeRubber -ingotRawRubber -ingotStyreneButadieneRubber -ingotReinforcedEpoxyResin -ingotPolyvinylChloride -ingotPolyphenyleneSulfide -ingotPolybenzimidazole -ingotPlastic -ingotEpoxy -ingotPolycaprolactam -ingotPolytetrafluoroethylene -ingotRubber -ingotPolyvinylButyral -ingotBlackSteel -ingotDamascusSteel -ingotTungstenSteel -ingotCobaltBrass -ingotSteelMagnetic -ingotVanadiumSteel -ingotPotin -ingotBorosilicateGlass -ingotNaquadahAlloy -ingotRhodiumPlatedPalladium -ingotRedSteel -ingotBlueSteel -ingotHssg -ingotHsse -ingotHsss -ingotBlueAlloy -ingotStellite100 -ingotWatertightSteel -ingotMaragingSteel300 -ingotHastelloyC276 -ingotHastelloyX -ingotTrinaquadalloy -ingotZeron100 -ingotTitaniumCarbide -ingotTantalumCarbide -ingotMolybdenumDisilicide -ingotHslaSteel -ingotTitaniumTungstenCarbide -ingotIncoloyMa956 -ingotHotDraconium -ingotHotSignalum -ingotHotLumium -ingotHotEnderium -ingotHotTaranium -ingotHotEuropium -ingotHotIridium -ingotHotNiobium -ingotHotOsmium -ingotHotPalladium -ingotHotRhodium -ingotHotRuthenium -ingotHotSamarium -ingotHotSilicon -ingotHotTitanium -ingotHotTungsten -ingotHotVanadium -ingotHotYttrium -ingotHotNaquadah -ingotHotNaquadahEnriched -ingotHotNaquadria -ingotHotTrinium -ingotHotKanthal -ingotHotNichrome -ingotHotNiobiumNitride -ingotHotNiobiumTitanium -ingotHotBlackBronze -ingotHotRuridit -ingotHotUltimet -ingotHotVanadiumGallium -ingotHotYttriumBariumCuprate -ingotHotOsmiridium -ingotHotTungstenCarbide -ingotHotMagnesiumDiboride -ingotHotMercuryBariumCalciumCuprate -ingotHotUraniumTriplatinum -ingotHotSamariumIronArsenicOxide -ingotHotIndiumTinBariumTitaniumCuprate -ingotHotUraniumRhodiumDinaquadide -ingotHotEnrichedNaquadahTriniumEuropiumDuranide -ingotHotRutheniumTriniumAmericiumNeutronate -ingotHotRtmAlloy -ingotHotTungstenSteel -ingotHotNaquadahAlloy -ingotHotRhodiumPlatedPalladium -ingotHotHssg -ingotHotHsse -ingotHotHsss -ingotHotStellite100 -ingotHotWatertightSteel -ingotHotMaragingSteel300 -ingotHotHastelloyC276 -ingotHotHastelloyX -ingotHotTrinaquadalloy -ingotHotZeron100 -ingotHotTitaniumCarbide -ingotHotTantalumCarbide -ingotHotMolybdenumDisilicide -ingotHotTitaniumTungstenCarbide -ingotHotIncoloyMa956 -plateDraconium -plateAwakenedDraconium -plateDarkSteel -plateArdite -plateManyullyn -plateConductiveIron -plateEnergeticAlloy -plateVibrantAlloy -platePulsatingIron -plateElectricalSteel -plateCrystalMatrix -plateSoularium -plateEndSteel -plateMicroversium -plateKaptonK -plateTaranium -plateAluminium -plateAmericium -plateBeryllium -plateChrome -plateCobalt -plateDarmstadtium -plateEuropium -plateGallium -plateManganese -plateMolybdenum -plateNeodymium -plateOsmium -platePalladium -platePlutonium241 -plateRhodium -plateRuthenium -plateTantalum -plateThorium -plateTungsten -plateUranium -plateUranium238 -plateUranium235 -plateZinc -plateNaquadah -plateNaquadahEnriched -plateNaquadria -plateNeutronium -plateTritanium -plateDuranium -plateTrinium -plateCertusQuartz -plateAnnealedCopper -plateBatteryAlloy -plateBlueTopaz -plateBrass -plateCupronickel -plateDiamond -plateEmerald -plateGreenSapphire -plateKanthal -plateLazurite -plateMagnalium -plateNichrome -plateNiobiumNitride -plateNiobiumTitanium -plateObsidian -plateSterlingSilver -plateRoseGold -plateBlackBronze -plateBismuthBronze -plateRuridit -plateRuby -plateSapphire -plateSodalite -plateStainlessSteel -plateTinAlloy -plateTopaz -plateUltimet -plateVanadiumGallium -plateWroughtIron -plateYttriumBariumCuprate -plateNetherQuartz -plateQuartzite -plateGraphene -plateOsmiridium -plateGalliumArsenide -plateIndiumGalliumPhosphide -plateTungstenCarbide -plateEnderPearl -plateIndiumTinBariumTitaniumCuprate -plateUraniumRhodiumDinaquadide -plateEnrichedNaquadahTriniumEuropiumDuranide -plateSiliconeRubber -plateStyreneButadieneRubber -plateReinforcedEpoxyResin -platePolyvinylChloride -platePolyphenyleneSulfide -platePolybenzimidazole -platePlastic -plateEpoxy -platePolycaprolactam -platePolytetrafluoroethylene -plateRubber -platePolyvinylButyral -plateStone -plateGlowstone -plateNetherStar -plateWood -plateTreatedWood -plateGlass -plateOlivine -plateOpal -plateAmethyst -plateLapis -plateBlackSteel -plateDamascusSteel -plateTungstenSteel -plateCobaltBrass -plateGarnetRed -plateGarnetYellow -plateVanadiumSteel -platePotin -plateNaquadahAlloy -plateRhodiumPlatedPalladium -plateRedstone -plateEnderEye -plateRedSteel -plateBlueSteel -plateHssg -plateRedAlloy -plateHsse -plateHsss -plateBlueAlloy -plateStellite100 -plateWatertightSteel -plateMaragingSteel300 -plateHastelloyC276 -plateHastelloyX -plateTrinaquadalloy -plateZeron100 -plateTitaniumCarbide -plateTantalumCarbide -plateMolybdenumDisilicide -plateHslaSteel -plateTitaniumTungstenCarbide -plateIncoloyMa956 -plateDoubleDraconium -plateDoubleAwakenedDraconium -plateDoubleSignalum -plateDoubleEnergeticAlloy -plateDoubleVibrantAlloy -plateDoubleLumium -plateDoubleEnderium -plateDoubleElectrumFlux -plateDoubleCrystalMatrix -plateDoubleAluminium -plateDoubleAmericium -plateDoubleBeryllium -plateDoubleChrome -plateDoubleCobalt -plateDoubleCopper -plateDoubleDarmstadtium -plateDoubleEuropium -plateDoubleGold -plateDoubleIridium -plateDoubleLead -plateDoubleNickel -plateDoubleOsmium -plateDoublePlatinum -plateDoublePlutonium241 -plateDoubleSilver -plateDoubleTin -plateDoubleTitanium -plateDoubleTungsten -plateDoubleNaquadah -plateDoubleNaquadria -plateDoubleNeutronium -plateDoubleDuranium -plateDoubleTrinium -plateDoubleBrass -plateDoubleBronze -plateDoubleCupronickel -plateDoubleElectrum -plateDoubleMagnalium -plateDoubleNiobiumTitanium -plateDoubleSterlingSilver -plateDoubleRoseGold -plateDoubleBlackBronze -plateDoubleStainlessSteel -plateDoubleSteel -plateDoubleTinAlloy -plateDoubleUltimet -plateDoubleWroughtIron -plateDoubleOsmiridium -plateDoubleTungstenCarbide -plateDoubleTungstenSteel -plateDoubleCobaltBrass -plateDoubleVanadiumSteel -plateDoublePotin -plateDoubleNaquadahAlloy -plateDoubleRhodiumPlatedPalladium -plateDoubleWatertightSteel -plateDoubleTrinaquadalloy -plateDoubleMolybdenumDisilicide -plateDoubleHslaSteel -plateDoubleIncoloyMa956 -plateDenseDraconium -plateDenseSignalum -plateDenseTaranium -plateDenseDarmstadtium -plateDenseIridium -plateDenseNeutronium -plateDenseTritanium -plateDenseTrinium -plateDenseObsidian -plateDenseTungstenSteel -plateDenseNaquadahAlloy -plateDenseRhodiumPlatedPalladium -plateDenseTantalumCarbide -foilLumium -foilEnderium -foilAluminium -foilAmericium -foilCobalt -foilCopper -foilEuropium -foilGallium -foilGold -foilIridium -foilLead -foilManganese -foilMolybdenum -foilOsmium -foilPalladium -foilPlatinum -foilRhodium -foilRuthenium -foilSilicon -foilSilver -foilTantalum -foilTin -foilTitanium -foilTungsten -foilZinc -foilNaquadah -foilNaquadahEnriched -foilNaquadria -foilTritanium -foilDuranium -foilTrinium -foilAnnealedCopper -foilBronze -foilCupronickel -foilElectrum -foilNiobiumNitride -foilNiobiumTitanium -foilRuridit -foilStainlessSteel -foilSteel -foilVanadiumGallium -foilWroughtIron -foilYttriumBariumCuprate -foilGraphene -foilOsmiridium -foilTungstenCarbide -foilIndiumTinBariumTitaniumCuprate -foilUraniumRhodiumDinaquadide -foilEnrichedNaquadahTriniumEuropiumDuranide -foilSiliconeRubber -foilStyreneButadieneRubber -foilPolyvinylChloride -foilPolyphenyleneSulfide -foilPolybenzimidazole -foilPlastic -foilPolycaprolactam -foilPolytetrafluoroethylene -foilRubber -foilBlackSteel -foilTungstenSteel -foilVanadiumSteel -foilNaquadahAlloy -foilHssg -foilRedAlloy -foilHsss -stickDraconium -stickAwakenedDraconium -stickDarkSteel -stickSignalum -stickConductiveIron -stickEnergeticAlloy -stickVibrantAlloy -stickPulsatingIron -stickElectricalSteel -stickLumium -stickEnderium -stickElectrumFlux -stickMithril -stickEndSteel -stickMicroversium -stickBerylliumOxide -stickAluminium -stickAmericium -stickBerkelium -stickChrome -stickCobalt -stickDarmstadtium -stickEuropium -stickGold -stickLead -stickManganese -stickMolybdenum -stickNeodymium -stickNickel -stickOsmium -stickPalladium -stickPlatinum -stickPlutonium241 -stickRhodium -stickRuthenium -stickSamarium -stickSilver -stickThorium -stickTin -stickTungsten -stickUranium -stickUranium238 -stickUranium235 -stickZinc -stickNaquadah -stickNaquadahEnriched -stickNaquadria -stickNeutronium -stickTritanium -stickDuranium -stickTrinium -stickAnnealedCopper -stickBatteryAlloy -stickBlueTopaz -stickBrass -stickBronze -stickCupronickel -stickDiamond -stickElectrum -stickEmerald -stickGreenSapphire -stickInvar -stickKanthal -stickLazurite -stickMagnalium -stickNichrome -stickNiobiumNitride -stickNiobiumTitanium -stickSterlingSilver -stickRoseGold -stickBlackBronze -stickBismuthBronze -stickRuridit -stickRuby -stickSapphire -stickSodalite -stickStainlessSteel -stickTinAlloy -stickTopaz -stickUltimet -stickVanadiumGallium -stickWroughtIron -stickYttriumBariumCuprate -stickOsmiridium -stickNickelZincFerrite -stickIronMagnetic -stickTungstenCarbide -stickNeodymiumMagnetic -stickSamariumMagnetic -stickRtmAlloy -stickSiliconeRubber -stickStyreneButadieneRubber -stickPolyvinylChloride -stickPolyphenyleneSulfide -stickPolytetrafluoroethylene -stickRubber -stickStone -stickTreatedWood -stickOlivine -stickOpal -stickAmethyst -stickLapis -stickApatite -stickBlackSteel -stickDamascusSteel -stickTungstenSteel -stickCobaltBrass -stickGarnetRed -stickGarnetYellow -stickSteelMagnetic -stickVanadiumSteel -stickPotin -stickNaquadahAlloy -stickRhodiumPlatedPalladium -stickRedSteel -stickBlueSteel -stickHssg -stickRedAlloy -stickHsse -stickHsss -stickBlueAlloy -stickWatertightSteel -stickMaragingSteel300 -stickHastelloyC276 -stickHastelloyX -stickMolybdenumDisilicide -stickHslaSteel -stickIncoloyMa956 -stickLongAluminium -stickLongCopper -stickLongDarmstadtium -stickLongEuropium -stickLongGold -stickLongIridium -stickLongIron -stickLongLead -stickLongOsmium -stickLongPlatinum -stickLongRhodium -stickLongSamarium -stickLongSilver -stickLongTin -stickLongTitanium -stickLongTungsten -stickLongNaquadah -stickLongNeutronium -stickLongTritanium -stickLongAnnealedCopper -stickLongBrass -stickLongBronze -stickLongCupronickel -stickLongElectrum -stickLongInvar -stickLongKanthal -stickLongMagnalium -stickLongNichrome -stickLongNiobiumTitanium -stickLongSterlingSilver -stickLongRoseGold -stickLongBlackBronze -stickLongBismuthBronze -stickLongRuridit -stickLongStainlessSteel -stickLongSteel -stickLongTinAlloy -stickLongUltimet -stickLongVanadiumGallium -stickLongWroughtIron -stickLongYttriumBariumCuprate -stickLongOsmiridium -stickLongTungstenCarbide -stickLongSamariumMagnetic -stickLongRtmAlloy -stickLongWood -stickLongTungstenSteel -stickLongCobaltBrass -stickLongVanadiumSteel -stickLongPotin -stickLongNaquadahAlloy -stickLongRhodiumPlatedPalladium -stickLongHssg -stickLongHsse -stickLongHsss -stickLongMolybdenumDisilicide -stickLongHslaSteel -boltVibrantAlloy -boltAluminium -boltChrome -boltDarmstadtium -boltGold -boltIridium -boltIron -boltLead -boltManganese -boltMolybdenum -boltNeodymium -boltOsmium -boltPlatinum -boltRhodium -boltSilver -boltTin -boltTitanium -boltTungsten -boltNaquadah -boltNaquadahEnriched -boltNaquadria -boltNeutronium -boltTritanium -boltTrinium -boltAnnealedCopper -boltBrass -boltBronze -boltDiamond -boltElectrum -boltInvar -boltMagnalium -boltNiobiumTitanium -boltSterlingSilver -boltRoseGold -boltBlackBronze -boltBismuthBronze -boltRuridit -boltStainlessSteel -boltSteel -boltTinAlloy -boltUltimet -boltWroughtIron -boltYttriumBariumCuprate -boltOsmiridium -boltIronMagnetic -boltTungstenCarbide -boltRubber -boltWood -boltApatite -boltTungstenSteel -boltCobaltBrass -boltVanadiumSteel -boltPotin -boltNaquadahAlloy -boltRhodiumPlatedPalladium -boltHssg -boltRedAlloy -boltHsse -boltHsss -boltBlueAlloy -screwVibrantAlloy -screwAluminium -screwChrome -screwDarmstadtium -screwGold -screwIridium -screwIron -screwLead -screwManganese -screwMolybdenum -screwNeodymium -screwOsmium -screwPlatinum -screwRhodium -screwSilver -screwTin -screwTitanium -screwTungsten -screwNaquadah -screwNaquadahEnriched -screwNaquadria -screwNeutronium -screwTritanium -screwTrinium -screwAnnealedCopper -screwBrass -screwBronze -screwDiamond -screwElectrum -screwInvar -screwMagnalium -screwNiobiumTitanium -screwSterlingSilver -screwRoseGold -screwBlackBronze -screwBismuthBronze -screwRuridit -screwStainlessSteel -screwSteel -screwTinAlloy -screwUltimet -screwWroughtIron -screwYttriumBariumCuprate -screwOsmiridium -screwIronMagnetic -screwTungstenCarbide -screwRubber -screwWood -screwApatite -screwTungstenSteel -screwCobaltBrass -screwVanadiumSteel -screwPotin -screwNaquadahAlloy -screwRhodiumPlatedPalladium -screwHssg -screwRedAlloy -screwHsse -screwHsss -screwBlueAlloy -ringAwakenedDraconium -ringBerylliumOxide -ringAluminium -ringChrome -ringDarmstadtium -ringGold -ringIridium -ringIron -ringLead -ringPlatinum -ringSilver -ringTin -ringTitanium -ringZinc -ringTritanium -ringBronze -ringElectrum -ringRoseGold -ringStainlessSteel -ringSteel -ringWroughtIron -ringOsmiridium -ringNickelZincFerrite -ringSiliconeRubber -ringStyreneButadieneRubber -ringRubber -ringTungstenSteel -ringNaquadahAlloy -ringRhodiumPlatedPalladium -ringHsse -ringHsss -ringMolybdenumDisilicide -nuggetAwakenedDraconium -nuggetOmnium -nuggetArdite -nuggetManyullyn -nuggetCrystalMatrix -nuggetInfinity -nuggetMicroversium -nuggetDraconicSuperconductor -nuggetBerylliumOxide -nuggetKaptonK -nuggetTaranium -nuggetAluminium -nuggetAmericium -nuggetAntimony -nuggetBerkelium -nuggetBeryllium -nuggetBismuth -nuggetCalifornium -nuggetChrome -nuggetCobalt -nuggetCurium -nuggetDarmstadtium -nuggetEinsteinium -nuggetEuropium -nuggetGallium -nuggetIndium -nuggetLutetium -nuggetManganese -nuggetMolybdenum -nuggetNeodymium -nuggetNeptunium -nuggetNiobium -nuggetOsmium -nuggetPalladium -nuggetPlutonium -nuggetRhodium -nuggetRuthenium -nuggetSamarium -nuggetTantalum -nuggetThorium -nuggetTungsten -nuggetUranium -nuggetVanadium -nuggetYttrium -nuggetZinc -nuggetNaquadah -nuggetNaquadahEnriched -nuggetNaquadria -nuggetNeutronium -nuggetTritanium -nuggetDuranium -nuggetTrinium -nuggetAnnealedCopper -nuggetBatteryAlloy -nuggetBrass -nuggetCupronickel -nuggetKanthal -nuggetMagnalium -nuggetNichrome -nuggetNiobiumNitride -nuggetNiobiumTitanium -nuggetSterlingSilver -nuggetRoseGold -nuggetBlackBronze -nuggetBismuthBronze -nuggetRuridit -nuggetSolderingAlloy -nuggetStainlessSteel -nuggetTinAlloy -nuggetUltimet -nuggetVanadiumGallium -nuggetWroughtIron -nuggetYttriumBariumCuprate -nuggetGraphite -nuggetOsmiridium -nuggetGalliumArsenide -nuggetIndiumGalliumPhosphide -nuggetNickelZincFerrite -nuggetIronMagnetic -nuggetTungstenCarbide -nuggetNeodymiumMagnetic -nuggetSamariumMagnetic -nuggetManganesePhosphide -nuggetMagnesiumDiboride -nuggetMercuryBariumCalciumCuprate -nuggetUraniumTriplatinum -nuggetSamariumIronArsenicOxide -nuggetIndiumTinBariumTitaniumCuprate -nuggetUraniumRhodiumDinaquadide -nuggetEnrichedNaquadahTriniumEuropiumDuranide -nuggetRutheniumTriniumAmericiumNeutronate -nuggetRtmAlloy -nuggetSiliconeRubber -nuggetRawRubber -nuggetStyreneButadieneRubber -nuggetReinforcedEpoxyResin -nuggetPolyvinylChloride -nuggetPolyphenyleneSulfide -nuggetPolybenzimidazole -nuggetPlastic -nuggetEpoxy -nuggetPolycaprolactam -nuggetPolytetrafluoroethylene -nuggetRubber -nuggetPolyvinylButyral -nuggetBlackSteel -nuggetDamascusSteel -nuggetTungstenSteel -nuggetCobaltBrass -nuggetSteelMagnetic -nuggetVanadiumSteel -nuggetPotin -nuggetBorosilicateGlass -nuggetNaquadahAlloy -nuggetRhodiumPlatedPalladium -nuggetRedSteel -nuggetBlueSteel -nuggetHssg -nuggetRedAlloy -nuggetHsse -nuggetHsss -nuggetBlueAlloy -nuggetStellite100 -nuggetWatertightSteel -nuggetMaragingSteel300 -nuggetHastelloyC276 -nuggetHastelloyX -nuggetTrinaquadalloy -nuggetZeron100 -nuggetTitaniumCarbide -nuggetTantalumCarbide -nuggetMolybdenumDisilicide -nuggetHslaSteel -nuggetTitaniumTungstenCarbide -nuggetIncoloyMa956 -roundNeutronium -roundTritanium -roundOsmiridium -roundHsss -springAluminium -springCopper -springEuropium -springGold -springIron -springLead -springTin -springTungsten -springNaquadah -springNeutronium -springTritanium -springCupronickel -springKanthal -springNichrome -springNiobiumTitanium -springSteel -springVanadiumGallium -springYttriumBariumCuprate -springRtmAlloy -springTungstenSteel -springNaquadahAlloy -springHssg -springMolybdenumDisilicide -springHslaSteel -springSmallAluminium -springSmallCopper -springSmallGold -springSmallIron -springSmallLead -springSmallTin -springSmallTungsten -springSmallNiobiumTitanium -springSmallSteel -springSmallVanadiumGallium -springSmallYttriumBariumCuprate -gearDraconium -gearAwakenedDraconium -gearConductiveIron -gearEnergeticAlloy -gearVibrantAlloy -gearPulsatingIron -gearElectricalSteel -gearEndSteel -gearAluminium -gearRhodium -gearRuthenium -gearTungsten -gearNaquadria -gearNeutronium -gearTritanium -gearDuranium -gearTrinium -gearSterlingSilver -gearRoseGold -gearBlackBronze -gearRuridit -gearStainlessSteel -gearUltimet -gearWroughtIron -gearOsmiridium -gearTungstenCarbide -gearSiliconeRubber -gearRubber -gearBlackSteel -gearDamascusSteel -gearTungstenSteel -gearCobaltBrass -gearVanadiumSteel -gearPotin -gearNaquadahAlloy -gearRedSteel -gearBlueSteel -gearHssg -gearHsse -gearHsss -gearSmallAluminium -gearSmallDarmstadtium -gearSmallIron -gearSmallTitanium -gearSmallTritanium -gearSmallBronze -gearSmallStainlessSteel -gearSmallSteel -gearSmallOsmiridium -gearSmallTungstenCarbide -gearSmallTungstenSteel -gearSmallNaquadahAlloy -gearSmallRhodiumPlatedPalladium -gearSmallHssg -gearSmallHsss -wireFineLumium -wireFineEnderium -wireFineAluminium -wireFineAmericium -wireFineCobalt -wireFineCopper -wireFineEuropium -wireFineGold -wireFineIridium -wireFineLead -wireFinePalladium -wireFinePlatinum -wireFineRhodium -wireFineSilver -wireFineTantalum -wireFineTin -wireFineTitanium -wireFineZinc -wireFineNaquadah -wireFineNaquadria -wireFineTritanium -wireFineAnnealedCopper -wireFineCupronickel -wireFineElectrum -wireFineNiobiumTitanium -wireFineRuridit -wireFineStainlessSteel -wireFineSteel -wireFineYttriumBariumCuprate -wireFineOsmiridium -wireFineIndiumTinBariumTitaniumCuprate -wireFineUraniumRhodiumDinaquadide -wireFineEnrichedNaquadahTriniumEuropiumDuranide -wireFineBlackSteel -wireFineTungstenSteel -wireFineBorosilicateGlass -wireFineHssg -wireFineRedAlloy -rotorChrome -rotorDarmstadtium -rotorIridium -rotorIron -rotorLead -rotorTin -rotorTitanium -rotorBronze -rotorStainlessSteel -rotorSteel -rotorOsmiridium -rotorTungstenSteel -rotorNaquadahAlloy -rotorRhodiumPlatedPalladium -rotorHsss -lensBlueTopaz -lensDiamond -lensEmerald -lensRuby -lensSapphire -lensTopaz -lensEnderPearl -lensNetherStar -lensGlass -turbineBladeAluminium -turbineBladeChrome -turbineBladeIridium -turbineBladeIron -turbineBladeManganese -turbineBladeMolybdenum -turbineBladeNeodymium -turbineBladeOsmium -turbineBladeTitanium -turbineBladeTungsten -turbineBladeNaquadah -turbineBladeNeutronium -turbineBladeTritanium -turbineBladeBrass -turbineBladeBronze -turbineBladeInvar -turbineBladeMagnalium -turbineBladeSterlingSilver -turbineBladeRoseGold -turbineBladeBlackBronze -turbineBladeBismuthBronze -turbineBladeStainlessSteel -turbineBladeSteel -turbineBladeUltimet -turbineBladeWroughtIron -turbineBladeOsmiridium -turbineBladeTungstenCarbide -turbineBladeTungstenSteel -turbineBladeCobaltBrass -turbineBladeVanadiumSteel -turbineBladeNaquadahAlloy -turbineBladeRhodiumPlatedPalladium -turbineBladeHssg -turbineBladeHsse -turbineBladeHsss -toolHeadDrillEndSteel -toolHeadDrillAluminium -toolHeadDrillIron -toolHeadDrillTitanium -toolHeadDrillNeutronium -toolHeadDrillDuranium -toolHeadDrillBronze -toolHeadDrillDiamond -toolHeadDrillInvar -toolHeadDrillSterlingSilver -toolHeadDrillRoseGold -toolHeadDrillStainlessSteel -toolHeadDrillSteel -toolHeadDrillUltimet -toolHeadDrillWroughtIron -toolHeadDrillTungstenCarbide -toolHeadDrillDamascusSteel -toolHeadDrillTungstenSteel -toolHeadDrillCobaltBrass -toolHeadDrillVanadiumSteel -toolHeadDrillNaquadahAlloy -toolHeadDrillRedSteel -toolHeadDrillBlueSteel -toolHeadDrillHsse -toolHeadChainsawEndSteel -toolHeadChainsawAluminium -toolHeadChainsawIron -toolHeadChainsawTitanium -toolHeadChainsawNeutronium -toolHeadChainsawDuranium -toolHeadChainsawBronze -toolHeadChainsawDiamond -toolHeadChainsawInvar -toolHeadChainsawSterlingSilver -toolHeadChainsawRoseGold -toolHeadChainsawStainlessSteel -toolHeadChainsawSteel -toolHeadChainsawUltimet -toolHeadChainsawWroughtIron -toolHeadChainsawTungstenCarbide -toolHeadChainsawDamascusSteel -toolHeadChainsawTungstenSteel -toolHeadChainsawCobaltBrass -toolHeadChainsawVanadiumSteel -toolHeadChainsawNaquadahAlloy -toolHeadChainsawRedSteel -toolHeadChainsawBlueSteel -toolHeadChainsawHsse -toolHeadWrenchEndSteel -toolHeadWrenchAluminium -toolHeadWrenchIron -toolHeadWrenchTitanium -toolHeadWrenchNeutronium -toolHeadWrenchDuranium -toolHeadWrenchBronze -toolHeadWrenchDiamond -toolHeadWrenchInvar -toolHeadWrenchSterlingSilver -toolHeadWrenchRoseGold -toolHeadWrenchStainlessSteel -toolHeadWrenchSteel -toolHeadWrenchUltimet -toolHeadWrenchWroughtIron -toolHeadWrenchTungstenCarbide -toolHeadWrenchDamascusSteel -toolHeadWrenchTungstenSteel -toolHeadWrenchCobaltBrass -toolHeadWrenchVanadiumSteel -toolHeadWrenchNaquadahAlloy -toolHeadWrenchRedSteel -toolHeadWrenchBlueSteel -toolHeadWrenchHsse -toolHeadBuzzSawEndSteel -toolHeadBuzzSawAluminium -toolHeadBuzzSawIron -toolHeadBuzzSawTitanium -toolHeadBuzzSawNeutronium -toolHeadBuzzSawDuranium -toolHeadBuzzSawBronze -toolHeadBuzzSawDiamond -toolHeadBuzzSawInvar -toolHeadBuzzSawSterlingSilver -toolHeadBuzzSawRoseGold -toolHeadBuzzSawStainlessSteel -toolHeadBuzzSawSteel -toolHeadBuzzSawUltimet -toolHeadBuzzSawWroughtIron -toolHeadBuzzSawTungstenCarbide -toolHeadBuzzSawDamascusSteel -toolHeadBuzzSawTungstenSteel -toolHeadBuzzSawCobaltBrass -toolHeadBuzzSawVanadiumSteel -toolHeadBuzzSawNaquadahAlloy -toolHeadBuzzSawRedSteel -toolHeadBuzzSawBlueSteel -toolHeadBuzzSawHsse -toolHeadScrewdriverAluminium -toolHeadScrewdriverIron -toolHeadScrewdriverTitanium -toolHeadScrewdriverNeutronium -toolHeadScrewdriverBronze -toolHeadScrewdriverInvar -toolHeadScrewdriverSterlingSilver -toolHeadScrewdriverRoseGold -toolHeadScrewdriverStainlessSteel -toolHeadScrewdriverSteel -toolHeadScrewdriverUltimet -toolHeadScrewdriverWroughtIron -toolHeadScrewdriverTungstenCarbide -toolHeadScrewdriverTungstenSteel -toolHeadScrewdriverCobaltBrass -toolHeadScrewdriverVanadiumSteel -toolHeadScrewdriverNaquadahAlloy -toolHeadScrewdriverHsse -gemPerfectDiamond -gemPerfectEmerald -gemPerfectRuby -gemPerfectTopaz -craftingLensMagenta -craftingLensGlass -craftingLensLime -craftingLensYellow -craftingLensBrown -craftingLensGray -craftingLensBlue -craftingLensLightGray -craftingLensLightBlue -craftingLensBlack -craftingLensGreen -craftingLensPurple -craftingLensCyan -craftingLensRed -craftingLensPink -craftingLensOrange -toolSword -toolPickaxe -toolShovel -toolAxe -toolSaw -craftingToolSaw -toolHammer -craftingToolHardHammer -toolMallet -craftingToolSoftHammer -toolMiningHammer -toolSpade -toolWrench -craftingToolWrench -toolFile -craftingToolFile -toolCrowbar -craftingToolCrowbar -toolScrewdriver -craftingToolScrewdriver -toolMortar -craftingToolMortar -toolWireCutter -craftingToolWireCutter -toolScythe -toolKnife -craftingToolKnife -toolButcheryKnife -craftingToolButcheryKnife -toolDrill -toolChainsaw -toolBuzzsaw -toolPlunger -lampGtWhite -lampGtOrange -lampGtMagenta -lampGtLightBlue -lampGtYellow -lampGtLime -lampGtPink -lampGtGray -lampGt -lampGtCyan -lampGtPurple -lampGtBlue -lampGtBrown -lampGtGreen -lampGtRed -lampGtBlack -blockPlastic -blockArdite -blockMagnalium -blockLutetium -blockNiobiumTitanium -blockBerkelium -blockWroughtIron -blockTrinium -blockPolybenzimidazole -blockPolyvinylButyral -blockCupronickel -blockReinforcedEpoxyResin -blockThorium -blockTrinaquadalloy -blockDamascusSteel -blockNaquadahEnriched -blockSugar -blockNaquadahAlloy -blockCobaltBrass -blockIndium -blockOmnium -blockHsss -blockYttrium -blockIndiumGalliumPhosphide -blockBlueTopaz -blockVanadium -blockFlint -blockWatertightSteel -blockAndradite -blockEuropium -blockBlueAlloy -blockNiobium -blockCurium -blockSodalite -blockOpal -blockStainlessSteel -blockAmethyst -blockAnnealedCopper -blockMagnesiumDiboride -blockDarmstadtium -blockNickelZincFerrite -blockStellite100 -blockIncoloyMa956 -blockRockSalt -blockPolyphenyleneSulfide -blockManganesePhosphide -blockPlutonium241 -blockRuridit -blockSalt -blockEinsteinium -blockNeodymium -blockSterlingSilver -blockUranium235 -blockAntimony -blockEnderEye -blockSapphire -blockTitaniumCarbide -blockManyullyn -blockIronMagnetic -blockTopaz -blockVanadiumSteel -blockVanadiumGallium -blockTinAlloy -blockGreenSapphire -blockSamariumMagnetic -blockOsmium -blockSolderingAlloy -blockEnrichedNaquadahTriniumEuropiumDuranide -blockHastelloyC276 -blockCoke -blockDulysite -blockRedSteel -blockBorosilicateGlass -blockRedAlloy -blockBatteryAlloy -blockCinnabar -blockTungsten -blockRhodium -blockRtmAlloy -blockPolytetrafluoroethylene -blockRutheniumTriniumAmericiumNeutronate -blockRhodiumPlatedPalladium -blockRoseGold -blockPlutonium -blockPotin -blockUraniumRhodiumDinaquadide -blockKanthal -blockCertusQuartz -blockChrome -blockTungstenSteel -blockSilicon -blockNaquadah -blockMicroversium -blockGalliumArsenide -blockRealgar -blockOlivine -blockZeron100 -blockLaurite -blockCalifornium -blockYttriumBariumCuprate -blockNichrome -blockMonazite -blockUvarovite -blockGarnetRed -blockGallium -blockNiobiumNitride -blockBlueSteel -blockTungstenCarbide -blockNeptunium -blockNaquadria -blockRuby -blockZinc -blockEpoxy -blockTaranium -blockPyrope -blockKaptonK -blockUltimet -blockGrossular -blockBlackSteel -blockQuartzite -blockPolycaprolactam -blockApatite -blockBismuthBronze -blockRuthenium -blockSteelMagnetic -blockHastelloyX -blockAlmandine -blockUraniumTriplatinum -blockMalachite -blockTantalum -blockPolyvinylChloride -blockTantalumCarbide -blockBrass -blockHssg -blockAluminium -blockRutile -blockTitaniumTungstenCarbide -blockMaragingSteel300 -blockSpessartine -blockBismuth -blockTritanium -blockRubber -blockGarnetYellow -blockMolybdenumDisilicide -blockNeutronium -blockMercuryBariumCalciumCuprate -blockOsmiridium -blockSiliconeRubber -blockSamariumIronArsenicOxide -blockMolybdenum -blockNeodymiumMagnetic -blockBerylliumOxide -blockHslaSteel -blockEnderPearl -blockNull -blockDuranium -blockPalladium -blockAwakenedDraconium -blockAmericium -blockStyreneButadieneRubber -blockSamarium -blockLazurite -blockDraconicSuperconductor -blockBlackBronze -blockIndiumTinBariumTitaniumCuprate -blockHsse -frameGtBlackSteel -frameGtWatertightSteel -frameGtDarkSteel -frameGtInvar -frameGtWood -frameGtTungsten -frameGtHslaSteel -frameGtPolytetrafluoroethylene -frameGtEuropium -frameGtBronze -frameGtNull -frameGtMaragingSteel300 -frameGtBerkelium -frameGtHastelloyX -frameGtTreatedWood -frameGtIridium -frameGtTungstenSteel -frameGtAwakenedDraconium -frameGtStainlessSteel -frameGtBlueSteel -frameGtTungstenCarbide -frameGtTritanium -frameGtTitanium -frameGtIncoloyMa956 -frameGtNaquadahAlloy -frameGtMicroversium -frameGtHastelloyC276 -frameGtRuridit -frameGtNeutronium -frameGtUltimet -frameGtHsse -frameGtHssg -frameGtHsss -frameGtAluminium -frameGtBrass -frameGtSteel -oreNetherrackDraconium -oreEndstoneDraconium -oreSandDraconium -oreRedSandDraconium -oreGraniteDraconium -oreDioriteDraconium -oreAndesiteDraconium -oreBlackgraniteDraconium -oreRedgraniteDraconium -oreMarbleDraconium -oreBasaltDraconium -oreOsmiridium8020 -oreNetherrackOsmiridium8020 -oreEndstoneOsmiridium8020 -oreSandOsmiridium8020 -oreRedSandOsmiridium8020 -oreGraniteOsmiridium8020 -oreDioriteOsmiridium8020 -oreAndesiteOsmiridium8020 -oreBlackgraniteOsmiridium8020 -oreRedgraniteOsmiridium8020 -oreMarbleOsmiridium8020 -oreBasaltOsmiridium8020 -oreIridosmine8020 -oreNetherrackIridosmine8020 -oreEndstoneIridosmine8020 -oreSandIridosmine8020 -oreRedSandIridosmine8020 -oreGraniteIridosmine8020 -oreDioriteIridosmine8020 -oreAndesiteIridosmine8020 -oreBlackgraniteIridosmine8020 -oreRedgraniteIridosmine8020 -oreMarbleIridosmine8020 -oreBasaltIridosmine8020 -oreKaemanite -oreNetherrackKaemanite -oreEndstoneKaemanite -oreSandKaemanite -oreRedSandKaemanite -oreGraniteKaemanite -oreDioriteKaemanite -oreAndesiteKaemanite -oreBlackgraniteKaemanite -oreRedgraniteKaemanite -oreMarbleKaemanite -oreBasaltKaemanite -oreFluorite -oreNetherrackFluorite -oreEndstoneFluorite -oreSandFluorite -oreRedSandFluorite -oreGraniteFluorite -oreDioriteFluorite -oreAndesiteFluorite -oreBlackgraniteFluorite -oreRedgraniteFluorite -oreMarbleFluorite -oreBasaltFluorite -oreSnowchestite -oreNetherrackSnowchestite -oreEndstoneSnowchestite -oreSandSnowchestite -oreRedSandSnowchestite -oreGraniteSnowchestite -oreDioriteSnowchestite -oreAndesiteSnowchestite -oreBlackgraniteSnowchestite -oreRedgraniteSnowchestite -oreMarbleSnowchestite -oreBasaltSnowchestite -oreDarmstadtite -oreNetherrackDarmstadtite -oreEndstoneDarmstadtite -oreSandDarmstadtite -oreRedSandDarmstadtite -oreGraniteDarmstadtite -oreDioriteDarmstadtite -oreAndesiteDarmstadtite -oreBlackgraniteDarmstadtite -oreRedgraniteDarmstadtite -oreMarbleDarmstadtite -oreBasaltDarmstadtite -oreDulysite -oreNetherrackDulysite -oreEndstoneDulysite -oreSandDulysite -oreRedSandDulysite -oreGraniteDulysite -oreDioriteDulysite -oreAndesiteDulysite -oreBlackgraniteDulysite -oreRedgraniteDulysite -oreMarbleDulysite -oreBasaltDulysite -oreLaurite -oreNetherrackLaurite -oreEndstoneLaurite -oreSandLaurite -oreRedSandLaurite -oreGraniteLaurite -oreDioriteLaurite -oreAndesiteLaurite -oreBlackgraniteLaurite -oreRedgraniteLaurite -oreMarbleLaurite -oreBasaltLaurite -oreCuprorhodsite -oreNetherrackCuprorhodsite -oreEndstoneCuprorhodsite -oreSandCuprorhodsite -oreRedSandCuprorhodsite -oreGraniteCuprorhodsite -oreDioriteCuprorhodsite -oreAndesiteCuprorhodsite -oreBlackgraniteCuprorhodsite -oreRedgraniteCuprorhodsite -oreMarbleCuprorhodsite -oreBasaltCuprorhodsite -oreAluminium -oreNetherrackAluminium -oreEndstoneAluminium -oreSandAluminium -oreRedSandAluminium -oreGraniteAluminium -oreDioriteAluminium -oreAndesiteAluminium -oreBlackgraniteAluminium -oreRedgraniteAluminium -oreMarbleAluminium -oreBasaltAluminium -oreBeryllium -oreNetherrackBeryllium -oreEndstoneBeryllium -oreSandBeryllium -oreRedSandBeryllium -oreGraniteBeryllium -oreDioriteBeryllium -oreAndesiteBeryllium -oreBlackgraniteBeryllium -oreRedgraniteBeryllium -oreMarbleBeryllium -oreBasaltBeryllium -oreNetherrackCobalt -oreEndstoneCobalt -oreSandCobalt -oreRedSandCobalt -oreGraniteCobalt -oreDioriteCobalt -oreAndesiteCobalt -oreBlackgraniteCobalt -oreRedgraniteCobalt -oreMarbleCobalt -oreBasaltCobalt -oreNetherrackCopper -oreEndstoneCopper -oreSandCopper -oreRedSandCopper -oreGraniteCopper -oreDioriteCopper -oreAndesiteCopper -oreBlackgraniteCopper -oreRedgraniteCopper -oreMarbleCopper -oreBasaltCopper -oreNetherrackGold -oreEndstoneGold -oreSandGold -oreRedSandGold -oreGraniteGold -oreDioriteGold -oreAndesiteGold -oreBlackgraniteGold -oreRedgraniteGold -oreMarbleGold -oreBasaltGold -oreNetherrackIron -oreEndstoneIron -oreSandIron -oreRedSandIron -oreGraniteIron -oreDioriteIron -oreAndesiteIron -oreBlackgraniteIron -oreRedgraniteIron -oreMarbleIron -oreBasaltIron -oreNetherrackLead -oreEndstoneLead -oreSandLead -oreRedSandLead -oreGraniteLead -oreDioriteLead -oreAndesiteLead -oreBlackgraniteLead -oreRedgraniteLead -oreMarbleLead -oreBasaltLead -oreLithium -oreNetherrackLithium -oreEndstoneLithium -oreSandLithium -oreRedSandLithium -oreGraniteLithium -oreDioriteLithium -oreAndesiteLithium -oreBlackgraniteLithium -oreRedgraniteLithium -oreMarbleLithium -oreBasaltLithium -oreMolybdenum -oreNetherrackMolybdenum -oreEndstoneMolybdenum -oreSandMolybdenum -oreRedSandMolybdenum -oreGraniteMolybdenum -oreDioriteMolybdenum -oreAndesiteMolybdenum -oreBlackgraniteMolybdenum -oreRedgraniteMolybdenum -oreMarbleMolybdenum -oreBasaltMolybdenum -oreNeodymium -oreNetherrackNeodymium -oreEndstoneNeodymium -oreSandNeodymium -oreRedSandNeodymium -oreGraniteNeodymium -oreDioriteNeodymium -oreAndesiteNeodymium -oreBlackgraniteNeodymium -oreRedgraniteNeodymium -oreMarbleNeodymium -oreBasaltNeodymium -oreNetherrackNickel -oreEndstoneNickel -oreSandNickel -oreRedSandNickel -oreGraniteNickel -oreDioriteNickel -oreAndesiteNickel -oreBlackgraniteNickel -oreRedgraniteNickel -oreMarbleNickel -oreBasaltNickel -orePalladium -oreNetherrackPalladium -oreEndstonePalladium -oreSandPalladium -oreRedSandPalladium -oreGranitePalladium -oreDioritePalladium -oreAndesitePalladium -oreBlackgranitePalladium -oreRedgranitePalladium -oreMarblePalladium -oreBasaltPalladium -oreNetherrackPlatinum -oreEndstonePlatinum -oreSandPlatinum -oreRedSandPlatinum -oreGranitePlatinum -oreDioritePlatinum -oreAndesitePlatinum -oreBlackgranitePlatinum -oreRedgranitePlatinum -oreMarblePlatinum -oreBasaltPlatinum -orePlutonium -oreNetherrackPlutonium -oreEndstonePlutonium -oreSandPlutonium -oreRedSandPlutonium -oreGranitePlutonium -oreDioritePlutonium -oreAndesitePlutonium -oreBlackgranitePlutonium -oreRedgranitePlutonium -oreMarblePlutonium -oreBasaltPlutonium -oreNetherrackSilver -oreEndstoneSilver -oreSandSilver -oreRedSandSilver -oreGraniteSilver -oreDioriteSilver -oreAndesiteSilver -oreBlackgraniteSilver -oreRedgraniteSilver -oreMarbleSilver -oreBasaltSilver -oreSulfur -oreNetherrackSulfur -oreEndstoneSulfur -oreSandSulfur -oreRedSandSulfur -oreGraniteSulfur -oreDioriteSulfur -oreAndesiteSulfur -oreBlackgraniteSulfur -oreRedgraniteSulfur -oreMarbleSulfur -oreBasaltSulfur -oreThorium -oreNetherrackThorium -oreEndstoneThorium -oreSandThorium -oreRedSandThorium -oreGraniteThorium -oreDioriteThorium -oreAndesiteThorium -oreBlackgraniteThorium -oreRedgraniteThorium -oreMarbleThorium -oreBasaltThorium -oreNetherrackTin -oreEndstoneTin -oreSandTin -oreRedSandTin -oreGraniteTin -oreDioriteTin -oreAndesiteTin -oreBlackgraniteTin -oreRedgraniteTin -oreMarbleTin -oreBasaltTin -oreNaquadah -oreNetherrackNaquadah -oreEndstoneNaquadah -oreSandNaquadah -oreRedSandNaquadah -oreGraniteNaquadah -oreDioriteNaquadah -oreAndesiteNaquadah -oreBlackgraniteNaquadah -oreRedgraniteNaquadah -oreMarbleNaquadah -oreBasaltNaquadah -oreCertusQuartz -oreNetherrackCertusQuartz -oreEndstoneCertusQuartz -oreSandCertusQuartz -oreRedSandCertusQuartz -oreGraniteCertusQuartz -oreDioriteCertusQuartz -oreAndesiteCertusQuartz -oreBlackgraniteCertusQuartz -oreRedgraniteCertusQuartz -oreMarbleCertusQuartz -oreBasaltCertusQuartz -oreAlmandine -oreNetherrackAlmandine -oreEndstoneAlmandine -oreSandAlmandine -oreRedSandAlmandine -oreGraniteAlmandine -oreDioriteAlmandine -oreAndesiteAlmandine -oreBlackgraniteAlmandine -oreRedgraniteAlmandine -oreMarbleAlmandine -oreBasaltAlmandine -oreAsbestos -oreNetherrackAsbestos -oreEndstoneAsbestos -oreSandAsbestos -oreRedSandAsbestos -oreGraniteAsbestos -oreDioriteAsbestos -oreAndesiteAsbestos -oreBlackgraniteAsbestos -oreRedgraniteAsbestos -oreMarbleAsbestos -oreBasaltAsbestos -oreBandedIron -oreNetherrackBandedIron -oreEndstoneBandedIron -oreSandBandedIron -oreRedSandBandedIron -oreGraniteBandedIron -oreDioriteBandedIron -oreAndesiteBandedIron -oreBlackgraniteBandedIron -oreRedgraniteBandedIron -oreMarbleBandedIron -oreBasaltBandedIron -oreBlueTopaz -oreNetherrackBlueTopaz -oreEndstoneBlueTopaz -oreSandBlueTopaz -oreRedSandBlueTopaz -oreGraniteBlueTopaz -oreDioriteBlueTopaz -oreAndesiteBlueTopaz -oreBlackgraniteBlueTopaz -oreRedgraniteBlueTopaz -oreMarbleBlueTopaz -oreBasaltBlueTopaz -oreBrownLimonite -oreNetherrackBrownLimonite -oreEndstoneBrownLimonite -oreSandBrownLimonite -oreRedSandBrownLimonite -oreGraniteBrownLimonite -oreDioriteBrownLimonite -oreAndesiteBrownLimonite -oreBlackgraniteBrownLimonite -oreRedgraniteBrownLimonite -oreMarbleBrownLimonite -oreBasaltBrownLimonite -oreCalcite -oreNetherrackCalcite -oreEndstoneCalcite -oreSandCalcite -oreRedSandCalcite -oreGraniteCalcite -oreDioriteCalcite -oreAndesiteCalcite -oreBlackgraniteCalcite -oreRedgraniteCalcite -oreMarbleCalcite -oreBasaltCalcite -oreCassiterite -oreNetherrackCassiterite -oreEndstoneCassiterite -oreSandCassiterite -oreRedSandCassiterite -oreGraniteCassiterite -oreDioriteCassiterite -oreAndesiteCassiterite -oreBlackgraniteCassiterite -oreRedgraniteCassiterite -oreMarbleCassiterite -oreBasaltCassiterite -oreCassiteriteSand -oreNetherrackCassiteriteSand -oreEndstoneCassiteriteSand -oreSandCassiteriteSand -oreRedSandCassiteriteSand -oreGraniteCassiteriteSand -oreDioriteCassiteriteSand -oreAndesiteCassiteriteSand -oreBlackgraniteCassiteriteSand -oreRedgraniteCassiteriteSand -oreMarbleCassiteriteSand -oreBasaltCassiteriteSand -oreChalcopyrite -oreNetherrackChalcopyrite -oreEndstoneChalcopyrite -oreSandChalcopyrite -oreRedSandChalcopyrite -oreGraniteChalcopyrite -oreDioriteChalcopyrite -oreAndesiteChalcopyrite -oreBlackgraniteChalcopyrite -oreRedgraniteChalcopyrite -oreMarbleChalcopyrite -oreBasaltChalcopyrite -oreChromite -oreNetherrackChromite -oreEndstoneChromite -oreSandChromite -oreRedSandChromite -oreGraniteChromite -oreDioriteChromite -oreAndesiteChromite -oreBlackgraniteChromite -oreRedgraniteChromite -oreMarbleChromite -oreBasaltChromite -oreCinnabar -oreNetherrackCinnabar -oreEndstoneCinnabar -oreSandCinnabar -oreRedSandCinnabar -oreGraniteCinnabar -oreDioriteCinnabar -oreAndesiteCinnabar -oreBlackgraniteCinnabar -oreRedgraniteCinnabar -oreMarbleCinnabar -oreBasaltCinnabar -oreNetherrackCoal -oreEndstoneCoal -oreSandCoal -oreRedSandCoal -oreGraniteCoal -oreDioriteCoal -oreAndesiteCoal -oreBlackgraniteCoal -oreRedgraniteCoal -oreMarbleCoal -oreBasaltCoal -oreCobaltite -oreNetherrackCobaltite -oreEndstoneCobaltite -oreSandCobaltite -oreRedSandCobaltite -oreGraniteCobaltite -oreDioriteCobaltite -oreAndesiteCobaltite -oreBlackgraniteCobaltite -oreRedgraniteCobaltite -oreMarbleCobaltite -oreBasaltCobaltite -oreCooperite -oreNetherrackCooperite -oreEndstoneCooperite -oreSandCooperite -oreRedSandCooperite -oreGraniteCooperite -oreDioriteCooperite -oreAndesiteCooperite -oreBlackgraniteCooperite -oreRedgraniteCooperite -oreMarbleCooperite -oreBasaltCooperite -oreNetherrackDiamond -oreEndstoneDiamond -oreSandDiamond -oreRedSandDiamond -oreGraniteDiamond -oreDioriteDiamond -oreAndesiteDiamond -oreBlackgraniteDiamond -oreRedgraniteDiamond -oreMarbleDiamond -oreBasaltDiamond -oreNetherrackEmerald -oreEndstoneEmerald -oreSandEmerald -oreRedSandEmerald -oreGraniteEmerald -oreDioriteEmerald -oreAndesiteEmerald -oreBlackgraniteEmerald -oreRedgraniteEmerald -oreMarbleEmerald -oreBasaltEmerald -oreGalena -oreNetherrackGalena -oreEndstoneGalena -oreSandGalena -oreRedSandGalena -oreGraniteGalena -oreDioriteGalena -oreAndesiteGalena -oreBlackgraniteGalena -oreRedgraniteGalena -oreMarbleGalena -oreBasaltGalena -oreGarnierite -oreNetherrackGarnierite -oreEndstoneGarnierite -oreSandGarnierite -oreRedSandGarnierite -oreGraniteGarnierite -oreDioriteGarnierite -oreAndesiteGarnierite -oreBlackgraniteGarnierite -oreRedgraniteGarnierite -oreMarbleGarnierite -oreBasaltGarnierite -oreGreenSapphire -oreNetherrackGreenSapphire -oreEndstoneGreenSapphire -oreSandGreenSapphire -oreRedSandGreenSapphire -oreGraniteGreenSapphire -oreDioriteGreenSapphire -oreAndesiteGreenSapphire -oreBlackgraniteGreenSapphire -oreRedgraniteGreenSapphire -oreMarbleGreenSapphire -oreBasaltGreenSapphire -oreGrossular -oreNetherrackGrossular -oreEndstoneGrossular -oreSandGrossular -oreRedSandGrossular -oreGraniteGrossular -oreDioriteGrossular -oreAndesiteGrossular -oreBlackgraniteGrossular -oreRedgraniteGrossular -oreMarbleGrossular -oreBasaltGrossular -oreIlmenite -oreNetherrackIlmenite -oreEndstoneIlmenite -oreSandIlmenite -oreRedSandIlmenite -oreGraniteIlmenite -oreDioriteIlmenite -oreAndesiteIlmenite -oreBlackgraniteIlmenite -oreRedgraniteIlmenite -oreMarbleIlmenite -oreBasaltIlmenite -oreBauxite -oreNetherrackBauxite -oreEndstoneBauxite -oreSandBauxite -oreRedSandBauxite -oreGraniteBauxite -oreDioriteBauxite -oreAndesiteBauxite -oreBlackgraniteBauxite -oreRedgraniteBauxite -oreMarbleBauxite -oreBasaltBauxite -oreLazurite -oreNetherrackLazurite -oreEndstoneLazurite -oreSandLazurite -oreRedSandLazurite -oreGraniteLazurite -oreDioriteLazurite -oreAndesiteLazurite -oreBlackgraniteLazurite -oreRedgraniteLazurite -oreMarbleLazurite -oreBasaltLazurite -oreMagnesite -oreNetherrackMagnesite -oreEndstoneMagnesite -oreSandMagnesite -oreRedSandMagnesite -oreGraniteMagnesite -oreDioriteMagnesite -oreAndesiteMagnesite -oreBlackgraniteMagnesite -oreRedgraniteMagnesite -oreMarbleMagnesite -oreBasaltMagnesite -oreMagnetite -oreNetherrackMagnetite -oreEndstoneMagnetite -oreSandMagnetite -oreRedSandMagnetite -oreGraniteMagnetite -oreDioriteMagnetite -oreAndesiteMagnetite -oreBlackgraniteMagnetite -oreRedgraniteMagnetite -oreMarbleMagnetite -oreBasaltMagnetite -oreMolybdenite -oreNetherrackMolybdenite -oreEndstoneMolybdenite -oreSandMolybdenite -oreRedSandMolybdenite -oreGraniteMolybdenite -oreDioriteMolybdenite -oreAndesiteMolybdenite -oreBlackgraniteMolybdenite -oreRedgraniteMolybdenite -oreMarbleMolybdenite -oreBasaltMolybdenite -orePowellite -oreNetherrackPowellite -oreEndstonePowellite -oreSandPowellite -oreRedSandPowellite -oreGranitePowellite -oreDioritePowellite -oreAndesitePowellite -oreBlackgranitePowellite -oreRedgranitePowellite -oreMarblePowellite -oreBasaltPowellite -orePyrite -oreNetherrackPyrite -oreEndstonePyrite -oreSandPyrite -oreRedSandPyrite -oreGranitePyrite -oreDioritePyrite -oreAndesitePyrite -oreBlackgranitePyrite -oreRedgranitePyrite -oreMarblePyrite -oreBasaltPyrite -orePyrolusite -oreNetherrackPyrolusite -oreEndstonePyrolusite -oreSandPyrolusite -oreRedSandPyrolusite -oreGranitePyrolusite -oreDioritePyrolusite -oreAndesitePyrolusite -oreBlackgranitePyrolusite -oreRedgranitePyrolusite -oreMarblePyrolusite -oreBasaltPyrolusite -orePyrope -oreNetherrackPyrope -oreEndstonePyrope -oreSandPyrope -oreRedSandPyrope -oreGranitePyrope -oreDioritePyrope -oreAndesitePyrope -oreBlackgranitePyrope -oreRedgranitePyrope -oreMarblePyrope -oreBasaltPyrope -oreRockSalt -oreNetherrackRockSalt -oreEndstoneRockSalt -oreSandRockSalt -oreRedSandRockSalt -oreGraniteRockSalt -oreDioriteRockSalt -oreAndesiteRockSalt -oreBlackgraniteRockSalt -oreRedgraniteRockSalt -oreMarbleRockSalt -oreBasaltRockSalt -oreRuby -oreNetherrackRuby -oreEndstoneRuby -oreSandRuby -oreRedSandRuby -oreGraniteRuby -oreDioriteRuby -oreAndesiteRuby -oreBlackgraniteRuby -oreRedgraniteRuby -oreMarbleRuby -oreBasaltRuby -oreSalt -oreNetherrackSalt -oreEndstoneSalt -oreSandSalt -oreRedSandSalt -oreGraniteSalt -oreDioriteSalt -oreAndesiteSalt -oreBlackgraniteSalt -oreRedgraniteSalt -oreMarbleSalt -oreBasaltSalt -oreSaltpeter -oreNetherrackSaltpeter -oreEndstoneSaltpeter -oreSandSaltpeter -oreRedSandSaltpeter -oreGraniteSaltpeter -oreDioriteSaltpeter -oreAndesiteSaltpeter -oreBlackgraniteSaltpeter -oreRedgraniteSaltpeter -oreMarbleSaltpeter -oreBasaltSaltpeter -oreSapphire -oreNetherrackSapphire -oreEndstoneSapphire -oreSandSapphire -oreRedSandSapphire -oreGraniteSapphire -oreDioriteSapphire -oreAndesiteSapphire -oreBlackgraniteSapphire -oreRedgraniteSapphire -oreMarbleSapphire -oreBasaltSapphire -oreScheelite -oreNetherrackScheelite -oreEndstoneScheelite -oreSandScheelite -oreRedSandScheelite -oreGraniteScheelite -oreDioriteScheelite -oreAndesiteScheelite -oreBlackgraniteScheelite -oreRedgraniteScheelite -oreMarbleScheelite -oreBasaltScheelite -oreSodalite -oreNetherrackSodalite -oreEndstoneSodalite -oreSandSodalite -oreRedSandSodalite -oreGraniteSodalite -oreDioriteSodalite -oreAndesiteSodalite -oreBlackgraniteSodalite -oreRedgraniteSodalite -oreMarbleSodalite -oreBasaltSodalite -oreTantalite -oreNetherrackTantalite -oreEndstoneTantalite -oreSandTantalite -oreRedSandTantalite -oreGraniteTantalite -oreDioriteTantalite -oreAndesiteTantalite -oreBlackgraniteTantalite -oreRedgraniteTantalite -oreMarbleTantalite -oreBasaltTantalite -oreSpessartine -oreNetherrackSpessartine -oreEndstoneSpessartine -oreSandSpessartine -oreRedSandSpessartine -oreGraniteSpessartine -oreDioriteSpessartine -oreAndesiteSpessartine -oreBlackgraniteSpessartine -oreRedgraniteSpessartine -oreMarbleSpessartine -oreBasaltSpessartine -oreSphalerite -oreNetherrackSphalerite -oreEndstoneSphalerite -oreSandSphalerite -oreRedSandSphalerite -oreGraniteSphalerite -oreDioriteSphalerite -oreAndesiteSphalerite -oreBlackgraniteSphalerite -oreRedgraniteSphalerite -oreMarbleSphalerite -oreBasaltSphalerite -oreStibnite -oreNetherrackStibnite -oreEndstoneStibnite -oreSandStibnite -oreRedSandStibnite -oreGraniteStibnite -oreDioriteStibnite -oreAndesiteStibnite -oreBlackgraniteStibnite -oreRedgraniteStibnite -oreMarbleStibnite -oreBasaltStibnite -oreTetrahedrite -oreNetherrackTetrahedrite -oreEndstoneTetrahedrite -oreSandTetrahedrite -oreRedSandTetrahedrite -oreGraniteTetrahedrite -oreDioriteTetrahedrite -oreAndesiteTetrahedrite -oreBlackgraniteTetrahedrite -oreRedgraniteTetrahedrite -oreMarbleTetrahedrite -oreBasaltTetrahedrite -oreTopaz -oreNetherrackTopaz -oreEndstoneTopaz -oreSandTopaz -oreRedSandTopaz -oreGraniteTopaz -oreDioriteTopaz -oreAndesiteTopaz -oreBlackgraniteTopaz -oreRedgraniteTopaz -oreMarbleTopaz -oreBasaltTopaz -oreTungstate -oreNetherrackTungstate -oreEndstoneTungstate -oreSandTungstate -oreRedSandTungstate -oreGraniteTungstate -oreDioriteTungstate -oreAndesiteTungstate -oreBlackgraniteTungstate -oreRedgraniteTungstate -oreMarbleTungstate -oreBasaltTungstate -oreUraninite -oreNetherrackUraninite -oreEndstoneUraninite -oreSandUraninite -oreRedSandUraninite -oreGraniteUraninite -oreDioriteUraninite -oreAndesiteUraninite -oreBlackgraniteUraninite -oreRedgraniteUraninite -oreMarbleUraninite -oreBasaltUraninite -oreWulfenite -oreNetherrackWulfenite -oreEndstoneWulfenite -oreSandWulfenite -oreRedSandWulfenite -oreGraniteWulfenite -oreDioriteWulfenite -oreAndesiteWulfenite -oreBlackgraniteWulfenite -oreRedgraniteWulfenite -oreMarbleWulfenite -oreBasaltWulfenite -oreYellowLimonite -oreNetherrackYellowLimonite -oreEndstoneYellowLimonite -oreSandYellowLimonite -oreRedSandYellowLimonite -oreGraniteYellowLimonite -oreDioriteYellowLimonite -oreAndesiteYellowLimonite -oreBlackgraniteYellowLimonite -oreRedgraniteYellowLimonite -oreMarbleYellowLimonite -oreBasaltYellowLimonite -oreNetherQuartz -oreNetherrackNetherQuartz -oreEndstoneNetherQuartz -oreSandNetherQuartz -oreRedSandNetherQuartz -oreGraniteNetherQuartz -oreDioriteNetherQuartz -oreAndesiteNetherQuartz -oreBlackgraniteNetherQuartz -oreRedgraniteNetherQuartz -oreMarbleNetherQuartz -oreBasaltNetherQuartz -oreQuartzite -oreNetherrackQuartzite -oreEndstoneQuartzite -oreSandQuartzite -oreRedSandQuartzite -oreGraniteQuartzite -oreDioriteQuartzite -oreAndesiteQuartzite -oreBlackgraniteQuartzite -oreRedgraniteQuartzite -oreMarbleQuartzite -oreBasaltQuartzite -oreGraphite -oreNetherrackGraphite -oreEndstoneGraphite -oreSandGraphite -oreRedSandGraphite -oreGraniteGraphite -oreDioriteGraphite -oreAndesiteGraphite -oreBlackgraniteGraphite -oreRedgraniteGraphite -oreMarbleGraphite -oreBasaltGraphite -oreBornite -oreNetherrackBornite -oreEndstoneBornite -oreSandBornite -oreRedSandBornite -oreGraniteBornite -oreDioriteBornite -oreAndesiteBornite -oreBlackgraniteBornite -oreRedgraniteBornite -oreMarbleBornite -oreBasaltBornite -oreChalcocite -oreNetherrackChalcocite -oreEndstoneChalcocite -oreSandChalcocite -oreRedSandChalcocite -oreGraniteChalcocite -oreDioriteChalcocite -oreAndesiteChalcocite -oreBlackgraniteChalcocite -oreRedgraniteChalcocite -oreMarbleChalcocite -oreBasaltChalcocite -oreRealgar -oreNetherrackRealgar -oreEndstoneRealgar -oreSandRealgar -oreRedSandRealgar -oreGraniteRealgar -oreDioriteRealgar -oreAndesiteRealgar -oreBlackgraniteRealgar -oreRedgraniteRealgar -oreMarbleRealgar -oreBasaltRealgar -oreBastnasite -oreNetherrackBastnasite -oreEndstoneBastnasite -oreSandBastnasite -oreRedSandBastnasite -oreGraniteBastnasite -oreDioriteBastnasite -oreAndesiteBastnasite -oreBlackgraniteBastnasite -oreRedgraniteBastnasite -oreMarbleBastnasite -oreBasaltBastnasite -orePentlandite -oreNetherrackPentlandite -oreEndstonePentlandite -oreSandPentlandite -oreRedSandPentlandite -oreGranitePentlandite -oreDioritePentlandite -oreAndesitePentlandite -oreBlackgranitePentlandite -oreRedgranitePentlandite -oreMarblePentlandite -oreBasaltPentlandite -oreSpodumene -oreNetherrackSpodumene -oreEndstoneSpodumene -oreSandSpodumene -oreRedSandSpodumene -oreGraniteSpodumene -oreDioriteSpodumene -oreAndesiteSpodumene -oreBlackgraniteSpodumene -oreRedgraniteSpodumene -oreMarbleSpodumene -oreBasaltSpodumene -oreLepidolite -oreNetherrackLepidolite -oreEndstoneLepidolite -oreSandLepidolite -oreRedSandLepidolite -oreGraniteLepidolite -oreDioriteLepidolite -oreAndesiteLepidolite -oreBlackgraniteLepidolite -oreRedgraniteLepidolite -oreMarbleLepidolite -oreBasaltLepidolite -oreGlauconiteSand -oreNetherrackGlauconiteSand -oreEndstoneGlauconiteSand -oreSandGlauconiteSand -oreRedSandGlauconiteSand -oreGraniteGlauconiteSand -oreDioriteGlauconiteSand -oreAndesiteGlauconiteSand -oreBlackgraniteGlauconiteSand -oreRedgraniteGlauconiteSand -oreMarbleGlauconiteSand -oreBasaltGlauconiteSand -oreMalachite -oreNetherrackMalachite -oreEndstoneMalachite -oreSandMalachite -oreRedSandMalachite -oreGraniteMalachite -oreDioriteMalachite -oreAndesiteMalachite -oreBlackgraniteMalachite -oreRedgraniteMalachite -oreMarbleMalachite -oreBasaltMalachite -oreMica -oreNetherrackMica -oreEndstoneMica -oreSandMica -oreRedSandMica -oreGraniteMica -oreDioriteMica -oreAndesiteMica -oreBlackgraniteMica -oreRedgraniteMica -oreMarbleMica -oreBasaltMica -oreBarite -oreNetherrackBarite -oreEndstoneBarite -oreSandBarite -oreRedSandBarite -oreGraniteBarite -oreDioriteBarite -oreAndesiteBarite -oreBlackgraniteBarite -oreRedgraniteBarite -oreMarbleBarite -oreBasaltBarite -oreAlunite -oreNetherrackAlunite -oreEndstoneAlunite -oreSandAlunite -oreRedSandAlunite -oreGraniteAlunite -oreDioriteAlunite -oreAndesiteAlunite -oreBlackgraniteAlunite -oreRedgraniteAlunite -oreMarbleAlunite -oreBasaltAlunite -oreTalc -oreNetherrackTalc -oreEndstoneTalc -oreSandTalc -oreRedSandTalc -oreGraniteTalc -oreDioriteTalc -oreAndesiteTalc -oreBlackgraniteTalc -oreRedgraniteTalc -oreMarbleTalc -oreBasaltTalc -oreSoapstone -oreNetherrackSoapstone -oreEndstoneSoapstone -oreSandSoapstone -oreRedSandSoapstone -oreGraniteSoapstone -oreDioriteSoapstone -oreAndesiteSoapstone -oreBlackgraniteSoapstone -oreRedgraniteSoapstone -oreMarbleSoapstone -oreBasaltSoapstone -oreKyanite -oreNetherrackKyanite -oreEndstoneKyanite -oreSandKyanite -oreRedSandKyanite -oreGraniteKyanite -oreDioriteKyanite -oreAndesiteKyanite -oreBlackgraniteKyanite -oreRedgraniteKyanite -oreMarbleKyanite -oreBasaltKyanite -orePyrochlore -oreNetherrackPyrochlore -oreEndstonePyrochlore -oreSandPyrochlore -oreRedSandPyrochlore -oreGranitePyrochlore -oreDioritePyrochlore -oreAndesitePyrochlore -oreBlackgranitePyrochlore -oreRedgranitePyrochlore -oreMarblePyrochlore -oreBasaltPyrochlore -oreOilsands -oreNetherrackOilsands -oreEndstoneOilsands -oreSandOilsands -oreRedSandOilsands -oreGraniteOilsands -oreDioriteOilsands -oreAndesiteOilsands -oreBlackgraniteOilsands -oreRedgraniteOilsands -oreMarbleOilsands -oreBasaltOilsands -oreOlivine -oreNetherrackOlivine -oreEndstoneOlivine -oreSandOlivine -oreRedSandOlivine -oreGraniteOlivine -oreDioriteOlivine -oreAndesiteOlivine -oreBlackgraniteOlivine -oreRedgraniteOlivine -oreMarbleOlivine -oreBasaltOlivine -oreOpal -oreNetherrackOpal -oreEndstoneOpal -oreSandOpal -oreRedSandOpal -oreGraniteOpal -oreDioriteOpal -oreAndesiteOpal -oreBlackgraniteOpal -oreRedgraniteOpal -oreMarbleOpal -oreBasaltOpal -oreAmethyst -oreNetherrackAmethyst -oreEndstoneAmethyst -oreSandAmethyst -oreRedSandAmethyst -oreGraniteAmethyst -oreDioriteAmethyst -oreAndesiteAmethyst -oreBlackgraniteAmethyst -oreRedgraniteAmethyst -oreMarbleAmethyst -oreBasaltAmethyst -oreNetherrackLapis -oreEndstoneLapis -oreSandLapis -oreRedSandLapis -oreGraniteLapis -oreDioriteLapis -oreAndesiteLapis -oreBlackgraniteLapis -oreRedgraniteLapis -oreMarbleLapis -oreBasaltLapis -oreApatite -oreNetherrackApatite -oreEndstoneApatite -oreSandApatite -oreRedSandApatite -oreGraniteApatite -oreDioriteApatite -oreAndesiteApatite -oreBlackgraniteApatite -oreRedgraniteApatite -oreMarbleApatite -oreBasaltApatite -oreTricalciumPhosphate -oreNetherrackTricalciumPhosphate -oreEndstoneTricalciumPhosphate -oreSandTricalciumPhosphate -oreRedSandTricalciumPhosphate -oreGraniteTricalciumPhosphate -oreDioriteTricalciumPhosphate -oreAndesiteTricalciumPhosphate -oreBlackgraniteTricalciumPhosphate -oreRedgraniteTricalciumPhosphate -oreMarbleTricalciumPhosphate -oreBasaltTricalciumPhosphate -oreGarnetRed -oreNetherrackGarnetRed -oreEndstoneGarnetRed -oreSandGarnetRed -oreRedSandGarnetRed -oreGraniteGarnetRed -oreDioriteGarnetRed -oreAndesiteGarnetRed -oreBlackgraniteGarnetRed -oreRedgraniteGarnetRed -oreMarbleGarnetRed -oreBasaltGarnetRed -oreGarnetYellow -oreNetherrackGarnetYellow -oreEndstoneGarnetYellow -oreSandGarnetYellow -oreRedSandGarnetYellow -oreGraniteGarnetYellow -oreDioriteGarnetYellow -oreAndesiteGarnetYellow -oreBlackgraniteGarnetYellow -oreRedgraniteGarnetYellow -oreMarbleGarnetYellow -oreBasaltGarnetYellow -oreVanadiumMagnetite -oreNetherrackVanadiumMagnetite -oreEndstoneVanadiumMagnetite -oreSandVanadiumMagnetite -oreRedSandVanadiumMagnetite -oreGraniteVanadiumMagnetite -oreDioriteVanadiumMagnetite -oreAndesiteVanadiumMagnetite -oreBlackgraniteVanadiumMagnetite -oreRedgraniteVanadiumMagnetite -oreMarbleVanadiumMagnetite -oreBasaltVanadiumMagnetite -orePollucite -oreNetherrackPollucite -oreEndstonePollucite -oreSandPollucite -oreRedSandPollucite -oreGranitePollucite -oreDioritePollucite -oreAndesitePollucite -oreBlackgranitePollucite -oreRedgranitePollucite -oreMarblePollucite -oreBasaltPollucite -oreBentonite -oreNetherrackBentonite -oreEndstoneBentonite -oreSandBentonite -oreRedSandBentonite -oreGraniteBentonite -oreDioriteBentonite -oreAndesiteBentonite -oreBlackgraniteBentonite -oreRedgraniteBentonite -oreMarbleBentonite -oreBasaltBentonite -oreFullersEarth -oreNetherrackFullersEarth -oreEndstoneFullersEarth -oreSandFullersEarth -oreRedSandFullersEarth -oreGraniteFullersEarth -oreDioriteFullersEarth -oreAndesiteFullersEarth -oreBlackgraniteFullersEarth -oreRedgraniteFullersEarth -oreMarbleFullersEarth -oreBasaltFullersEarth -orePitchblende -oreNetherrackPitchblende -oreEndstonePitchblende -oreSandPitchblende -oreRedSandPitchblende -oreGranitePitchblende -oreDioritePitchblende -oreAndesitePitchblende -oreBlackgranitePitchblende -oreRedgranitePitchblende -oreMarblePitchblende -oreBasaltPitchblende -oreMonazite -oreNetherrackMonazite -oreEndstoneMonazite -oreSandMonazite -oreRedSandMonazite -oreGraniteMonazite -oreDioriteMonazite -oreAndesiteMonazite -oreBlackgraniteMonazite -oreRedgraniteMonazite -oreMarbleMonazite -oreBasaltMonazite -oreTrona -oreNetherrackTrona -oreEndstoneTrona -oreSandTrona -oreRedSandTrona -oreGraniteTrona -oreDioriteTrona -oreAndesiteTrona -oreBlackgraniteTrona -oreRedgraniteTrona -oreMarbleTrona -oreBasaltTrona -oreGypsum -oreNetherrackGypsum -oreEndstoneGypsum -oreSandGypsum -oreRedSandGypsum -oreGraniteGypsum -oreDioriteGypsum -oreAndesiteGypsum -oreBlackgraniteGypsum -oreRedgraniteGypsum -oreMarbleGypsum -oreBasaltGypsum -oreZeolite -oreNetherrackZeolite -oreEndstoneZeolite -oreSandZeolite -oreRedSandZeolite -oreGraniteZeolite -oreDioriteZeolite -oreAndesiteZeolite -oreBlackgraniteZeolite -oreRedgraniteZeolite -oreMarbleZeolite -oreBasaltZeolite -oreNetherrackRedstone -oreEndstoneRedstone -oreSandRedstone -oreRedSandRedstone -oreGraniteRedstone -oreDioriteRedstone -oreAndesiteRedstone -oreBlackgraniteRedstone -oreRedgraniteRedstone -oreMarbleRedstone -oreBasaltRedstone -oreElectrotine -oreNetherrackElectrotine -oreEndstoneElectrotine -oreSandElectrotine -oreRedSandElectrotine -oreGraniteElectrotine -oreDioriteElectrotine -oreAndesiteElectrotine -oreBlackgraniteElectrotine -oreRedgraniteElectrotine -oreMarbleElectrotine -oreBasaltElectrotine -oreDiatomite -oreNetherrackDiatomite -oreEndstoneDiatomite -oreSandDiatomite -oreRedSandDiatomite -oreGraniteDiatomite -oreDioriteDiatomite -oreAndesiteDiatomite -oreBlackgraniteDiatomite -oreRedgraniteDiatomite -oreMarbleDiatomite -oreBasaltDiatomite -oreGraniticMineralSand -oreNetherrackGraniticMineralSand -oreEndstoneGraniticMineralSand -oreSandGraniticMineralSand -oreRedSandGraniticMineralSand -oreGraniteGraniticMineralSand -oreDioriteGraniticMineralSand -oreAndesiteGraniticMineralSand -oreBlackgraniteGraniticMineralSand -oreRedgraniteGraniticMineralSand -oreMarbleGraniticMineralSand -oreBasaltGraniticMineralSand -oreGarnetSand -oreNetherrackGarnetSand -oreEndstoneGarnetSand -oreSandGarnetSand -oreRedSandGarnetSand -oreGraniteGarnetSand -oreDioriteGarnetSand -oreAndesiteGarnetSand -oreBlackgraniteGarnetSand -oreRedgraniteGarnetSand -oreMarbleGarnetSand -oreBasaltGarnetSand -oreBasalticMineralSand -oreNetherrackBasalticMineralSand -oreEndstoneBasalticMineralSand -oreSandBasalticMineralSand -oreRedSandBasalticMineralSand -oreGraniteBasalticMineralSand -oreDioriteBasalticMineralSand -oreAndesiteBasalticMineralSand -oreBlackgraniteBasalticMineralSand -oreRedgraniteBasalticMineralSand -oreMarbleBasalticMineralSand -oreBasaltBasalticMineralSand -wireGtSingleConductiveIron -wireGtSingleDraconicSuperconductor -wireGtSingleDraconium -wireGtSingleEndSteel -wireGtSingleEnderium -wireGtSingleEnergeticAlloy -wireGtSingleLumium -wireGtSingleMicroversium -wireGtSingleOmnium -wireGtSinglePulsatingIron -wireGtSingleSignalum -wireGtSingleVibrantAlloy -wireGtDoubleConductiveIron -wireGtDoubleDraconicSuperconductor -wireGtDoubleDraconium -wireGtDoubleEndSteel -wireGtDoubleEnderium -wireGtDoubleEnergeticAlloy -wireGtDoubleLumium -wireGtDoubleMicroversium -wireGtDoubleOmnium -wireGtDoublePulsatingIron -wireGtDoubleSignalum -wireGtDoubleVibrantAlloy -wireGtQuadrupleConductiveIron -wireGtQuadrupleDraconicSuperconductor -wireGtQuadrupleDraconium -wireGtQuadrupleEndSteel -wireGtQuadrupleEnderium -wireGtQuadrupleEnergeticAlloy -wireGtQuadrupleLumium -wireGtQuadrupleMicroversium -wireGtQuadrupleOmnium -wireGtQuadruplePulsatingIron -wireGtQuadrupleSignalum -wireGtQuadrupleVibrantAlloy -wireGtOctalConductiveIron -wireGtOctalDraconicSuperconductor -wireGtOctalDraconium -wireGtOctalEndSteel -wireGtOctalEnderium -wireGtOctalEnergeticAlloy -wireGtOctalLumium -wireGtOctalMicroversium -wireGtOctalOmnium -wireGtOctalPulsatingIron -wireGtOctalSignalum -wireGtOctalVibrantAlloy -wireGtHexConductiveIron -wireGtHexDraconicSuperconductor -wireGtHexDraconium -wireGtHexEndSteel -wireGtHexEnderium -wireGtHexEnergeticAlloy -wireGtHexLumium -wireGtHexMicroversium -wireGtHexOmnium -wireGtHexPulsatingIron -wireGtHexSignalum -wireGtHexVibrantAlloy -cableGtSingleMicroversium -cableGtDoubleMicroversium -cableGtQuadrupleMicroversium -cableGtOctalMicroversium -cableGtHexMicroversium -pipeTinyFluidLumium -pipeSmallFluidLumium -pipeNormalFluidLumium -pipeLargeFluidLumium -pipeHugeFluidLumium -pipeQuadrupleFluidLumium -pipeNonupleFluidLumium -pipeSmallItemSignalum -pipeNormalItemSignalum -pipeLargeItemSignalum -pipeHugeItemSignalum -pipeSmallRestrictiveSignalum -pipeNormalRestrictiveSignalum -pipeLargeRestrictiveSignalum -pipeHugeRestrictiveSignalum -wireGtSingleAluminium -wireGtSingleAnnealedCopper -wireGtSingleBlackSteel -wireGtSingleBlueAlloy -wireGtSingleCobalt -wireGtSingleCopper -wireGtSingleCupronickel -wireGtSingleElectrum -wireGtSingleEnrichedNaquadahTriniumEuropiumDuranide -wireGtSingleEuropium -wireGtSingleGold -wireGtSingleGraphene -wireGtSingleHssg -wireGtSingleIndiumTinBariumTitaniumCuprate -wireGtSingleIron -wireGtSingleKanthal -wireGtSingleLead -wireGtSingleMagnesiumDiboride -wireGtSingleManganesePhosphide -wireGtSingleMercuryBariumCalciumCuprate -wireGtSingleNaquadah -wireGtSingleNaquadahAlloy -wireGtSingleNichrome -wireGtSingleNickel -wireGtSingleNiobiumNitride -wireGtSingleNiobiumTitanium -wireGtSingleOsmium -wireGtSinglePlatinum -wireGtSingleRedAlloy -wireGtSingleRtmAlloy -wireGtSingleRutheniumTriniumAmericiumNeutronate -wireGtSingleSamariumIronArsenicOxide -wireGtSingleSilver -wireGtSingleSteel -wireGtSingleTin -wireGtSingleTrinium -wireGtSingleTritanium -wireGtSingleTungsten -wireGtSingleTungstenSteel -wireGtSingleUraniumRhodiumDinaquadide -wireGtSingleUraniumTriplatinum -wireGtSingleVanadiumGallium -wireGtSingleYttriumBariumCuprate -wireGtDoubleAluminium -wireGtDoubleAnnealedCopper -wireGtDoubleBlackSteel -wireGtDoubleBlueAlloy -wireGtDoubleCobalt -wireGtDoubleCopper -wireGtDoubleCupronickel -wireGtDoubleElectrum -wireGtDoubleEnrichedNaquadahTriniumEuropiumDuranide -wireGtDoubleEuropium -wireGtDoubleGold -wireGtDoubleGraphene -wireGtDoubleHssg -wireGtDoubleIndiumTinBariumTitaniumCuprate -wireGtDoubleIron -wireGtDoubleKanthal -wireGtDoubleLead -wireGtDoubleMagnesiumDiboride -wireGtDoubleManganesePhosphide -wireGtDoubleMercuryBariumCalciumCuprate -wireGtDoubleNaquadah -wireGtDoubleNaquadahAlloy -wireGtDoubleNichrome -wireGtDoubleNickel -wireGtDoubleNiobiumNitride -wireGtDoubleNiobiumTitanium -wireGtDoubleOsmium -wireGtDoublePlatinum -wireGtDoubleRedAlloy -wireGtDoubleRtmAlloy -wireGtDoubleRutheniumTriniumAmericiumNeutronate -wireGtDoubleSamariumIronArsenicOxide -wireGtDoubleSilver -wireGtDoubleSteel -wireGtDoubleTin -wireGtDoubleTrinium -wireGtDoubleTritanium -wireGtDoubleTungsten -wireGtDoubleTungstenSteel -wireGtDoubleUraniumRhodiumDinaquadide -wireGtDoubleUraniumTriplatinum -wireGtDoubleVanadiumGallium -wireGtDoubleYttriumBariumCuprate -wireGtQuadrupleAluminium -wireGtQuadrupleAnnealedCopper -wireGtQuadrupleBlackSteel -wireGtQuadrupleBlueAlloy -wireGtQuadrupleCobalt -wireGtQuadrupleCopper -wireGtQuadrupleCupronickel -wireGtQuadrupleElectrum -wireGtQuadrupleEnrichedNaquadahTriniumEuropiumDuranide -wireGtQuadrupleEuropium -wireGtQuadrupleGold -wireGtQuadrupleGraphene -wireGtQuadrupleHssg -wireGtQuadrupleIndiumTinBariumTitaniumCuprate -wireGtQuadrupleIron -wireGtQuadrupleKanthal -wireGtQuadrupleLead -wireGtQuadrupleMagnesiumDiboride -wireGtQuadrupleManganesePhosphide -wireGtQuadrupleMercuryBariumCalciumCuprate -wireGtQuadrupleNaquadah -wireGtQuadrupleNaquadahAlloy -wireGtQuadrupleNichrome -wireGtQuadrupleNickel -wireGtQuadrupleNiobiumNitride -wireGtQuadrupleNiobiumTitanium -wireGtQuadrupleOsmium -wireGtQuadruplePlatinum -wireGtQuadrupleRedAlloy -wireGtQuadrupleRtmAlloy -wireGtQuadrupleRutheniumTriniumAmericiumNeutronate -wireGtQuadrupleSamariumIronArsenicOxide -wireGtQuadrupleSilver -wireGtQuadrupleSteel -wireGtQuadrupleTin -wireGtQuadrupleTrinium -wireGtQuadrupleTritanium -wireGtQuadrupleTungsten -wireGtQuadrupleTungstenSteel -wireGtQuadrupleUraniumRhodiumDinaquadide -wireGtQuadrupleUraniumTriplatinum -wireGtQuadrupleVanadiumGallium -wireGtQuadrupleYttriumBariumCuprate -wireGtOctalAluminium -wireGtOctalAnnealedCopper -wireGtOctalBlackSteel -wireGtOctalBlueAlloy -wireGtOctalCobalt -wireGtOctalCopper -wireGtOctalCupronickel -wireGtOctalElectrum -wireGtOctalEnrichedNaquadahTriniumEuropiumDuranide -wireGtOctalEuropium -wireGtOctalGold -wireGtOctalGraphene -wireGtOctalHssg -wireGtOctalIndiumTinBariumTitaniumCuprate -wireGtOctalIron -wireGtOctalKanthal -wireGtOctalLead -wireGtOctalMagnesiumDiboride -wireGtOctalManganesePhosphide -wireGtOctalMercuryBariumCalciumCuprate -wireGtOctalNaquadah -wireGtOctalNaquadahAlloy -wireGtOctalNichrome -wireGtOctalNickel -wireGtOctalNiobiumNitride -wireGtOctalNiobiumTitanium -wireGtOctalOsmium -wireGtOctalPlatinum -wireGtOctalRedAlloy -wireGtOctalRtmAlloy -wireGtOctalRutheniumTriniumAmericiumNeutronate -wireGtOctalSamariumIronArsenicOxide -wireGtOctalSilver -wireGtOctalSteel -wireGtOctalTin -wireGtOctalTrinium -wireGtOctalTritanium -wireGtOctalTungsten -wireGtOctalTungstenSteel -wireGtOctalUraniumRhodiumDinaquadide -wireGtOctalUraniumTriplatinum -wireGtOctalVanadiumGallium -wireGtOctalYttriumBariumCuprate -wireGtHexAluminium -wireGtHexAnnealedCopper -wireGtHexBlackSteel -wireGtHexBlueAlloy -wireGtHexCobalt -wireGtHexCopper -wireGtHexCupronickel -wireGtHexElectrum -wireGtHexEnrichedNaquadahTriniumEuropiumDuranide -wireGtHexEuropium -wireGtHexGold -wireGtHexGraphene -wireGtHexHssg -wireGtHexIndiumTinBariumTitaniumCuprate -wireGtHexIron -wireGtHexKanthal -wireGtHexLead -wireGtHexMagnesiumDiboride -wireGtHexManganesePhosphide -wireGtHexMercuryBariumCalciumCuprate -wireGtHexNaquadah -wireGtHexNaquadahAlloy -wireGtHexNichrome -wireGtHexNickel -wireGtHexNiobiumNitride -wireGtHexNiobiumTitanium -wireGtHexOsmium -wireGtHexPlatinum -wireGtHexRedAlloy -wireGtHexRtmAlloy -wireGtHexRutheniumTriniumAmericiumNeutronate -wireGtHexSamariumIronArsenicOxide -wireGtHexSilver -wireGtHexSteel -wireGtHexTin -wireGtHexTrinium -wireGtHexTritanium -wireGtHexTungsten -wireGtHexTungstenSteel -wireGtHexUraniumRhodiumDinaquadide -wireGtHexUraniumTriplatinum -wireGtHexVanadiumGallium -wireGtHexYttriumBariumCuprate -cableGtSingleAluminium -cableGtSingleAnnealedCopper -cableGtSingleBlackSteel -cableGtSingleBlueAlloy -cableGtSingleCobalt -cableGtSingleCopper -cableGtSingleCupronickel -cableGtSingleElectrum -cableGtSingleEuropium -cableGtSingleGold -cableGtSingleGraphene -cableGtSingleHssg -cableGtSingleIron -cableGtSingleKanthal -cableGtSingleLead -cableGtSingleNaquadah -cableGtSingleNaquadahAlloy -cableGtSingleNichrome -cableGtSingleNickel -cableGtSingleNiobiumNitride -cableGtSingleNiobiumTitanium -cableGtSingleOsmium -cableGtSinglePlatinum -cableGtSingleRedAlloy -cableGtSingleRtmAlloy -cableGtSingleSilver -cableGtSingleSteel -cableGtSingleTin -cableGtSingleTrinium -cableGtSingleTritanium -cableGtSingleTungsten -cableGtSingleTungstenSteel -cableGtSingleVanadiumGallium -cableGtSingleYttriumBariumCuprate -cableGtDoubleAluminium -cableGtDoubleAnnealedCopper -cableGtDoubleBlackSteel -cableGtDoubleBlueAlloy -cableGtDoubleCobalt -cableGtDoubleCopper -cableGtDoubleCupronickel -cableGtDoubleElectrum -cableGtDoubleEuropium -cableGtDoubleGold -cableGtDoubleGraphene -cableGtDoubleHssg -cableGtDoubleIron -cableGtDoubleKanthal -cableGtDoubleLead -cableGtDoubleNaquadah -cableGtDoubleNaquadahAlloy -cableGtDoubleNichrome -cableGtDoubleNickel -cableGtDoubleNiobiumNitride -cableGtDoubleNiobiumTitanium -cableGtDoubleOsmium -cableGtDoublePlatinum -cableGtDoubleRedAlloy -cableGtDoubleRtmAlloy -cableGtDoubleSilver -cableGtDoubleSteel -cableGtDoubleTin -cableGtDoubleTrinium -cableGtDoubleTritanium -cableGtDoubleTungsten -cableGtDoubleTungstenSteel -cableGtDoubleVanadiumGallium -cableGtDoubleYttriumBariumCuprate -cableGtQuadrupleAluminium -cableGtQuadrupleAnnealedCopper -cableGtQuadrupleBlackSteel -cableGtQuadrupleBlueAlloy -cableGtQuadrupleCobalt -cableGtQuadrupleCopper -cableGtQuadrupleCupronickel -cableGtQuadrupleElectrum -cableGtQuadrupleEuropium -cableGtQuadrupleGold -cableGtQuadrupleGraphene -cableGtQuadrupleHssg -cableGtQuadrupleIron -cableGtQuadrupleKanthal -cableGtQuadrupleLead -cableGtQuadrupleNaquadah -cableGtQuadrupleNaquadahAlloy -cableGtQuadrupleNichrome -cableGtQuadrupleNickel -cableGtQuadrupleNiobiumNitride -cableGtQuadrupleNiobiumTitanium -cableGtQuadrupleOsmium -cableGtQuadruplePlatinum -cableGtQuadrupleRedAlloy -cableGtQuadrupleRtmAlloy -cableGtQuadrupleSilver -cableGtQuadrupleSteel -cableGtQuadrupleTin -cableGtQuadrupleTrinium -cableGtQuadrupleTritanium -cableGtQuadrupleTungsten -cableGtQuadrupleTungstenSteel -cableGtQuadrupleVanadiumGallium -cableGtQuadrupleYttriumBariumCuprate -cableGtOctalAluminium -cableGtOctalAnnealedCopper -cableGtOctalBlackSteel -cableGtOctalBlueAlloy -cableGtOctalCobalt -cableGtOctalCopper -cableGtOctalCupronickel -cableGtOctalElectrum -cableGtOctalEuropium -cableGtOctalGold -cableGtOctalGraphene -cableGtOctalHssg -cableGtOctalIron -cableGtOctalKanthal -cableGtOctalLead -cableGtOctalNaquadah -cableGtOctalNaquadahAlloy -cableGtOctalNichrome -cableGtOctalNickel -cableGtOctalNiobiumNitride -cableGtOctalNiobiumTitanium -cableGtOctalOsmium -cableGtOctalPlatinum -cableGtOctalRedAlloy -cableGtOctalRtmAlloy -cableGtOctalSilver -cableGtOctalSteel -cableGtOctalTin -cableGtOctalTrinium -cableGtOctalTritanium -cableGtOctalTungsten -cableGtOctalTungstenSteel -cableGtOctalVanadiumGallium -cableGtOctalYttriumBariumCuprate -cableGtHexAluminium -cableGtHexAnnealedCopper -cableGtHexBlackSteel -cableGtHexBlueAlloy -cableGtHexCobalt -cableGtHexCopper -cableGtHexCupronickel -cableGtHexElectrum -cableGtHexEuropium -cableGtHexGold -cableGtHexGraphene -cableGtHexHssg -cableGtHexIron -cableGtHexKanthal -cableGtHexLead -cableGtHexNaquadah -cableGtHexNaquadahAlloy -cableGtHexNichrome -cableGtHexNickel -cableGtHexNiobiumNitride -cableGtHexNiobiumTitanium -cableGtHexOsmium -cableGtHexPlatinum -cableGtHexRedAlloy -cableGtHexRtmAlloy -cableGtHexSilver -cableGtHexSteel -cableGtHexTin -cableGtHexTrinium -cableGtHexTritanium -cableGtHexTungsten -cableGtHexTungstenSteel -cableGtHexVanadiumGallium -cableGtHexYttriumBariumCuprate -pipeTinyFluidAluminium -pipeTinyFluidBronze -pipeTinyFluidChrome -pipeTinyFluidCopper -pipeTinyFluidDuranium -pipeTinyFluidEuropium -pipeTinyFluidGold -pipeTinyFluidIridium -pipeTinyFluidLead -pipeTinyFluidNaquadah -pipeTinyFluidNeutronium -pipeTinyFluidNiobiumTitanium -pipeTinyFluidPlastic -pipeTinyFluidPolybenzimidazole -pipeTinyFluidPolytetrafluoroethylene -pipeTinyFluidPotin -pipeTinyFluidStainlessSteel -pipeTinyFluidSteel -pipeTinyFluidTinAlloy -pipeTinyFluidTitanium -pipeTinyFluidTungsten -pipeTinyFluidTungstenCarbide -pipeTinyFluidTungstenSteel -pipeTinyFluidVanadiumSteel -pipeSmallFluidAluminium -pipeSmallFluidBronze -pipeSmallFluidChrome -pipeSmallFluidCopper -pipeSmallFluidDuranium -pipeSmallFluidEuropium -pipeSmallFluidGold -pipeSmallFluidIridium -pipeSmallFluidLead -pipeSmallFluidNaquadah -pipeSmallFluidNeutronium -pipeSmallFluidNiobiumTitanium -pipeSmallFluidPlastic -pipeSmallFluidPolybenzimidazole -pipeSmallFluidPolytetrafluoroethylene -pipeSmallFluidPotin -pipeSmallFluidStainlessSteel -pipeSmallFluidSteel -pipeSmallFluidTinAlloy -pipeSmallFluidTitanium -pipeSmallFluidTreatedWood -pipeSmallFluidTungsten -pipeSmallFluidTungstenCarbide -pipeSmallFluidTungstenSteel -pipeSmallFluidVanadiumSteel -pipeSmallFluidWood -pipeNormalFluidAluminium -pipeNormalFluidBronze -pipeNormalFluidChrome -pipeNormalFluidCopper -pipeNormalFluidDuranium -pipeNormalFluidEuropium -pipeNormalFluidGold -pipeNormalFluidIridium -pipeNormalFluidLead -pipeNormalFluidNaquadah -pipeNormalFluidNeutronium -pipeNormalFluidNiobiumTitanium -pipeNormalFluidPlastic -pipeNormalFluidPolybenzimidazole -pipeNormalFluidPolytetrafluoroethylene -pipeNormalFluidPotin -pipeNormalFluidStainlessSteel -pipeNormalFluidSteel -pipeNormalFluidTinAlloy -pipeNormalFluidTitanium -pipeNormalFluidTreatedWood -pipeNormalFluidTungsten -pipeNormalFluidTungstenCarbide -pipeNormalFluidTungstenSteel -pipeNormalFluidVanadiumSteel -pipeNormalFluidWood -pipeLargeFluidAluminium -pipeLargeFluidBronze -pipeLargeFluidChrome -pipeLargeFluidCopper -pipeLargeFluidDuranium -pipeLargeFluidEuropium -pipeLargeFluidGold -pipeLargeFluidIridium -pipeLargeFluidLead -pipeLargeFluidNaquadah -pipeLargeFluidNeutronium -pipeLargeFluidNiobiumTitanium -pipeLargeFluidPlastic -pipeLargeFluidPolybenzimidazole -pipeLargeFluidPolytetrafluoroethylene -pipeLargeFluidPotin -pipeLargeFluidStainlessSteel -pipeLargeFluidSteel -pipeLargeFluidTinAlloy -pipeLargeFluidTitanium -pipeLargeFluidTreatedWood -pipeLargeFluidTungsten -pipeLargeFluidTungstenCarbide -pipeLargeFluidTungstenSteel -pipeLargeFluidVanadiumSteel -pipeLargeFluidWood -pipeHugeFluidAluminium -pipeHugeFluidBronze -pipeHugeFluidChrome -pipeHugeFluidCopper -pipeHugeFluidDuranium -pipeHugeFluidEuropium -pipeHugeFluidGold -pipeHugeFluidIridium -pipeHugeFluidLead -pipeHugeFluidNaquadah -pipeHugeFluidNeutronium -pipeHugeFluidNiobiumTitanium -pipeHugeFluidPlastic -pipeHugeFluidPolybenzimidazole -pipeHugeFluidPolytetrafluoroethylene -pipeHugeFluidPotin -pipeHugeFluidStainlessSteel -pipeHugeFluidSteel -pipeHugeFluidTinAlloy -pipeHugeFluidTitanium -pipeHugeFluidTungsten -pipeHugeFluidTungstenCarbide -pipeHugeFluidTungstenSteel -pipeHugeFluidVanadiumSteel -pipeQuadrupleFluidAluminium -pipeQuadrupleFluidBronze -pipeQuadrupleFluidChrome -pipeQuadrupleFluidCopper -pipeQuadrupleFluidDuranium -pipeQuadrupleFluidEuropium -pipeQuadrupleFluidGold -pipeQuadrupleFluidIridium -pipeQuadrupleFluidLead -pipeQuadrupleFluidNaquadah -pipeQuadrupleFluidNeutronium -pipeQuadrupleFluidNiobiumTitanium -pipeQuadrupleFluidPlastic -pipeQuadrupleFluidPolybenzimidazole -pipeQuadrupleFluidPolytetrafluoroethylene -pipeQuadrupleFluidPotin -pipeQuadrupleFluidStainlessSteel -pipeQuadrupleFluidSteel -pipeQuadrupleFluidTinAlloy -pipeQuadrupleFluidTitanium -pipeQuadrupleFluidTungsten -pipeQuadrupleFluidTungstenCarbide -pipeQuadrupleFluidTungstenSteel -pipeQuadrupleFluidVanadiumSteel -pipeNonupleFluidAluminium -pipeNonupleFluidBronze -pipeNonupleFluidChrome -pipeNonupleFluidCopper -pipeNonupleFluidDuranium -pipeNonupleFluidEuropium -pipeNonupleFluidGold -pipeNonupleFluidIridium -pipeNonupleFluidLead -pipeNonupleFluidNaquadah -pipeNonupleFluidNeutronium -pipeNonupleFluidNiobiumTitanium -pipeNonupleFluidPlastic -pipeNonupleFluidPolybenzimidazole -pipeNonupleFluidPolytetrafluoroethylene -pipeNonupleFluidPotin -pipeNonupleFluidStainlessSteel -pipeNonupleFluidSteel -pipeNonupleFluidTinAlloy -pipeNonupleFluidTitanium -pipeNonupleFluidTungsten -pipeNonupleFluidTungstenCarbide -pipeNonupleFluidTungstenSteel -pipeNonupleFluidVanadiumSteel -pipeSmallItemMagnalium -pipeSmallItemOsmiridium -pipeSmallItemCobaltBrass -pipeSmallItemTin -pipeSmallItemElectrum -pipeSmallItemSterlingSilver -pipeSmallItemCupronickel -pipeSmallItemAmericium -pipeSmallItemPolyvinylChloride -pipeSmallItemCobalt -pipeSmallItemUltimet -pipeSmallItemPlatinum -pipeSmallItemBrass -pipeSmallItemNickel -pipeSmallItemRoseGold -pipeSmallItemOsmium -pipeSmallItemBlackBronze -pipeNormalItemMagnalium -pipeNormalItemOsmiridium -pipeNormalItemCobaltBrass -pipeNormalItemTin -pipeNormalItemElectrum -pipeNormalItemSterlingSilver -pipeNormalItemCupronickel -pipeNormalItemAmericium -pipeNormalItemPolyvinylChloride -pipeNormalItemCobalt -pipeNormalItemUltimet -pipeNormalItemPlatinum -pipeNormalItemBrass -pipeNormalItemNickel -pipeNormalItemRoseGold -pipeNormalItemOsmium -pipeNormalItemBlackBronze -pipeLargeItemMagnalium -pipeLargeItemOsmiridium -pipeLargeItemCobaltBrass -pipeLargeItemTin -pipeLargeItemElectrum -pipeLargeItemSterlingSilver -pipeLargeItemCupronickel -pipeLargeItemAmericium -pipeLargeItemPolyvinylChloride -pipeLargeItemCobalt -pipeLargeItemUltimet -pipeLargeItemPlatinum -pipeLargeItemBrass -pipeLargeItemNickel -pipeLargeItemRoseGold -pipeLargeItemOsmium -pipeLargeItemBlackBronze -pipeHugeItemMagnalium -pipeHugeItemOsmiridium -pipeHugeItemCobaltBrass -pipeHugeItemTin -pipeHugeItemElectrum -pipeHugeItemSterlingSilver -pipeHugeItemCupronickel -pipeHugeItemAmericium -pipeHugeItemPolyvinylChloride -pipeHugeItemCobalt -pipeHugeItemUltimet -pipeHugeItemPlatinum -pipeHugeItemBrass -pipeHugeItemNickel -pipeHugeItemRoseGold -pipeHugeItemOsmium -pipeHugeItemBlackBronze -pipeSmallRestrictiveMagnalium -pipeSmallRestrictiveOsmiridium -pipeSmallRestrictiveCobaltBrass -pipeSmallRestrictiveTin -pipeSmallRestrictiveElectrum -pipeSmallRestrictiveSterlingSilver -pipeSmallRestrictiveCupronickel -pipeSmallRestrictiveAmericium -pipeSmallRestrictivePolyvinylChloride -pipeSmallRestrictiveCobalt -pipeSmallRestrictiveUltimet -pipeSmallRestrictivePlatinum -pipeSmallRestrictiveBrass -pipeSmallRestrictiveNickel -pipeSmallRestrictiveRoseGold -pipeSmallRestrictiveOsmium -pipeSmallRestrictiveBlackBronze -pipeNormalRestrictiveMagnalium -pipeNormalRestrictiveOsmiridium -pipeNormalRestrictiveCobaltBrass -pipeNormalRestrictiveTin -pipeNormalRestrictiveElectrum -pipeNormalRestrictiveSterlingSilver -pipeNormalRestrictiveCupronickel -pipeNormalRestrictiveAmericium -pipeNormalRestrictivePolyvinylChloride -pipeNormalRestrictiveCobalt -pipeNormalRestrictiveUltimet -pipeNormalRestrictivePlatinum -pipeNormalRestrictiveBrass -pipeNormalRestrictiveNickel -pipeNormalRestrictiveRoseGold -pipeNormalRestrictiveOsmium -pipeNormalRestrictiveBlackBronze -pipeLargeRestrictiveMagnalium -pipeLargeRestrictiveOsmiridium -pipeLargeRestrictiveCobaltBrass -pipeLargeRestrictiveTin -pipeLargeRestrictiveElectrum -pipeLargeRestrictiveSterlingSilver -pipeLargeRestrictiveCupronickel -pipeLargeRestrictiveAmericium -pipeLargeRestrictivePolyvinylChloride -pipeLargeRestrictiveCobalt -pipeLargeRestrictiveUltimet -pipeLargeRestrictivePlatinum -pipeLargeRestrictiveBrass -pipeLargeRestrictiveNickel -pipeLargeRestrictiveRoseGold -pipeLargeRestrictiveOsmium -pipeLargeRestrictiveBlackBronze -pipeHugeRestrictiveMagnalium -pipeHugeRestrictiveOsmiridium -pipeHugeRestrictiveCobaltBrass -pipeHugeRestrictiveTin -pipeHugeRestrictiveElectrum -pipeHugeRestrictiveSterlingSilver -pipeHugeRestrictiveCupronickel -pipeHugeRestrictiveAmericium -pipeHugeRestrictivePolyvinylChloride -pipeHugeRestrictiveCobalt -pipeHugeRestrictiveUltimet -pipeHugeRestrictivePlatinum -pipeHugeRestrictiveBrass -pipeHugeRestrictiveNickel -pipeHugeRestrictiveRoseGold -pipeHugeRestrictiveOsmium -pipeHugeRestrictiveBlackBronze -blockClay -blockBrick -ingotClay -gemFlint -plankTreatedWood -craftingLensWhite -gemEnderEye -gemEnderPearl -gemCoal -gemCharcoal -gemNetherQuartz -gemNetherStar -platePaper -dustSugar -dustGunpowder -dustBone -stickBone -stickBlaze -blockNetherQuartz -blockBone -blockObsidian -stoneGraniteBlack -stoneGraniteRed -craftingAnvil -stoneObsidian -stoneMossy -stoneCobble -stoneSmooth -stoneBricks -stoneCracked -stoneChiseled -stoneNetherrack -stoneEndstone -craftingRedstoneTorch -craftingPiston -craftingFurnace -craftingFeather -itemWheat -paperEmpty -paperMap -bookEmpty -bookWritable -bookWritten -bookEnchanted -craftingBook -doorTreatedWood -slabTreatedWood -fenceTreatedWood -fenceGateTreatedWood -stairTreatedWood -stoneSoapstone -stoneRedrock -dustSand -cropCactus -cropMushroomRed -cropMushroomBrown -particleCustomizer -ingot_dark_soularium -h -ingotMagnesium -blockMagnesium -ingotLithium -ingotBoron -blockNetherstar -blockSkystone -dustMoon -crystalDilithium -questbookBrewery -questbookDistillery -questbookPolarizer -questbookAirCollector -questbookCanning -questbookSifter -questbookLvCef -questbookLvBatteryBuffer -questbookMvCef -questbookParallelControlHatch -questbookPowerUnit -questbookMacerator -questbookWirelessTerminals -questbookChisels -questbookScanner -questbookWA -questbookLathe -questbookAutoclave -questbookTanks -questbookNeptunium -questbookPlutonium -questbookAmericium -questbookCurium -questbookBerkelium -questbookCalifornium -questbookItemPipes -dustPulsating -meshPulsating -gemFluix -wrenches -denseOreRedstone -denseOreDiamond -denseOreIron -denseOreLapis -denseOreEmerald -denseOreMagma -denseOreOilsands -denseOreGold -denseOreCoal -barsIron -blockHopper -itemCoal -itemCharcoal -pearlEnderEye -itemBlazeRod -itemBlazePowder -itemClay -itemFlint -itemGhastTear -itemLeather -blockSlimeCongealed -ingotElectrical -gemLavaCrystal -ingotLavaCrystal -gemChargedLavaCrystal -ingotChargedLavaCrystal -gemInfusedLavaCrystal -ingotInfusedLavaCrystal -oreLavaCrystal -blockLavaCrystal -blockInfusedLavaCrystal -blockCompressedLavaCrystal -blockCompressedInfusedLavaCrystal -blockElectrical -blockCompressedObsidian -blockLavaInfusedObsidian -blockInfusedObsidian -apWorkbench -workbenchTier1 -workbenchTierOne -apHighTechBench -workbenchTier2 -workbenchTierTwo -apUltiTechBench -workbenchTier3 -workbenchTierThree -apChampionBench -workbenchTier4 -workbenchTierFour -stonebrickWhite -stonebrickWhiteCorner -stonebrickWhiteTower -stonebrickWhiteWall -stonebrickRed -stonebrickRedCorner -stonebrickRedTower -stonebrickRedWall -stonebrickBlack -stonebrickBlackCorner -stonebrickBlackTower -stonebrickBlackWall -stonebrickBlue -stonebrickBlueCorner -stonebrickBlueTower -stonebrickBlueWall -stonebrickGreen -stonebrickGreenCorner -stonebrickGreenTower -stonebrickGreenWall -stonebrickYellow -stonebrickYellowCorner -stonebrickYellowTower -stonebrickYellowWall -stonebrickPurple -stonebrickPurpleCorner -stonebrickPurpleTower -stonebrickPurpleWall -chainmail -scaleGuardian -guardianScale -witherBone -scaleEnderDragon -enderDragonScale -dragonScale -materialTheUltimate -materialUltimate -itemArrow -arrow -stonebrick -hopper -blockMagma -blockNetherWart -shulkerShell -shulkerBox -seed -rail -blockWither -ingotEnderiumBase -compressed1xDust -compressed2xDust -compressed1xEndStone -cropBarley -seedBarley -oreNetherIron -denseoreIron -oreNetherGold -denseoreGold -oreNetherCopper -denseoreCopper -oreNetherTin -denseoreTin -oreNetherLead -denseoreLead -oreNetherSilver -denseoreSilver -oreNetherNickel -denseoreNickel -oreNetherAluminium -denseoreAluminium -oreNetherAluminum -denseoreAluminum -oreNaturalAluminum -dustNaturalAluminum -oreNetherNaturalAluminum -denseoreNaturalAluminum -ingotNaturalAluminum -blockNaturalAluminum -oreNetherCoal -denseoreCoal -oreNetherRedstone -denseoreRedstone -oreNetherDiamond -denseoreDiamond -oreNetherEmerald -denseoreEmerald -oreNetherLapis -denseoreLapis -denseoreQuartz -oreNetherApatite -denseoreApatite -oreNetherCertusQuartz -denseoreCertusQuartz -oreChargedCertusQuartz -oreNetherChargedCertusQuartz -denseoreChargedCertusQuartz -oreNetherSulfur -denseoreSulfur -oreNetherSaltpeter -denseoreSaltpeter -oreNetherRuby -orePeridot -oreNetherPeridot -oreNetherTopaz -oreTanzanite -oreNetherTanzanite -oreNetherMalachite -oreNetherSapphire -oreAmber -oreNetherAmber -oreNetherAmethyst -oreManganese -oreNetherManganese -denseoreManganese -oreZinc -oreNetherZinc -denseoreZinc -oreNetherPlatinum -denseorePlatinum -oreIgnatius -dustIgnatius -oreNetherIgnatius -denseoreIgnatius -ingotIgnatius -blockIgnatius -oreShadowIron -dustShadowIron -oreNetherShadowIron -denseoreShadowIron -ingotShadowIron -blockShadowIron -oreLemurite -dustLemurite -oreNetherLemurite -denseoreLemurite -ingotLemurite -blockLemurite -oreMidasium -dustMidasium -oreNetherMidasium -denseoreMidasium -ingotMidasium -blockMidasium -oreVyroxeres -dustVyroxeres -oreNetherVyroxeres -denseoreVyroxeres -ingotVyroxeres -blockVyroxeres -oreCeruclase -dustCeruclase -oreNetherCeruclase -denseoreCeruclase -ingotCeruclase -blockCeruclase -oreKalendrite -dustKalendrite -oreNetherKalendrite -denseoreKalendrite -ingotKalendrite -blockKalendrite -oreVulcanite -dustVulcanite -oreNetherVulcanite -denseoreVulcanite -ingotVulcanite -blockVulcanite -oreSanguinite -dustSanguinite -oreNetherSanguinite -denseoreSanguinite -ingotSanguinite -blockSanguinite -orePrometheum -dustPrometheum -oreNetherPrometheum -denseorePrometheum -ingotPrometheum -blockPrometheum -oreDeepIron -dustDeepIron -oreNetherDeepIron -denseoreDeepIron -ingotDeepIron -blockDeepIron -oreInfuscolium -dustInfuscolium -oreNetherInfuscolium -denseoreInfuscolium -ingotInfuscolium -blockInfuscolium -oreOureclase -dustOureclase -oreNetherOureclase -denseoreOureclase -ingotOureclase -blockOureclase -oreAstralSilver -dustAstralSilver -oreNetherAstralSilver -denseoreAstralSilver -ingotAstralSilver -blockAstralSilver -oreCarmot -dustCarmot -oreNetherCarmot -denseoreCarmot -ingotCarmot -blockCarmot -oreNetherMithril -denseoreMithril -oreRubracium -dustRubracium -oreNetherRubracium -denseoreRubracium -ingotRubracium -blockRubracium -oreOrichalcum -dustOrichalcum -oreNetherOrichalcum -denseoreOrichalcum -ingotOrichalcum -blockOrichalcum -oreAdamantine -dustAdamantine -oreNetherAdamantine -denseoreAdamantine -ingotAdamantine -blockAdamantine -oreAtlarus -dustAtlarus -oreNetherAtlarus -denseoreAtlarus -ingotAtlarus -blockAtlarus -oreOsmium -oreNetherOsmium -denseoreOsmium -oreYellorite -dustYellorium -dustCyanite -oreNetherYellorite -denseoreYellorite -ingotYellorium -blockYellorium -denseoreArdite -denseoreCobalt -oreNetherQuartzBlack -denseoreQuartzBlack -oreUranium -oreNetherUranium -denseoreUranium -oreNetherSteel -oreNetherTitanium -denseoreTitanium -oreMagnesium -oreNetherMagnesium -denseoreMagnesium -oreTungsten -oreNetherTungsten -denseoreTungsten -oreNetherRutile -denseoreRutile -ingotRutile -oreNetherSalt -oreNetherDraconium -denseoreDraconium -oreProsperity -shardProsperity -oreNetherProsperity -oreEndProsperity -oreInferium -essenceInferium -oreNetherInferium -oreEndInferium -oreDimensionalShard -gemLava -gemEnderBiotite -oreBoron -oreCinnibar -dustCinnibar -crystalCinnibar -oreSheldonite -oreAstralStarmetal -dustAstralStarmetal -oreTritanium -oreEnder -dustRedstoneAlloy -ingotHepatizon -dustHepatizon -blockHepatizon -ingotAngmallen -dustAngmallen -blockAngmallen -ingotShadowSteel -dustShadowSteel -blockShadowSteel -ingotInolashite -dustInolashite -blockInolashite -ingotAmordrine -dustAmordrine -blockAmordrine -ingotQuicksilver -dustQuicksilver -blockQuicksilver -ingotHaderoth -dustHaderoth -blockHaderoth -ingotCelenegil -dustCelenegil -blockCelenegil -ingotTartarite -dustTartarite -blockTartarite -ingotCyanite -blockCyanite -ingotBlutonium -dustBlutonium -blockBlutonium -ingotLudicrite -dustLudicrite -blockLudicrite -ingotAluminumBrass -dustAluminumBrass -blockAluminumBrass -pulpWood -stairsWood -oreGarnet -gemGarnet -oreHeliodor -gemHeliodor -oreBeyrl -gemBeyrl -oreIndicolite -gemIndicolite -oreAquamarine -gemAquamarine -oreIolite -gemIolite -oreAgate -gemAgate -oreMorganite -gemMorganite -oreOnyx -gemOnyx -oreCarnelian -gemCarnelian -oreSpinel -gemSpinel -oreCitrine -gemCitrine -oreJasper -gemJasper -oreGoldenBeryl -gemGoldenBeryl -oreMoldavite -gemMoldavite -oreTurquoise -gemTurquoise -oreMoonstone -gemMoonstone -oreVioletSapphire -gemVioletSapphire -gemLepidolite -oreAmetrine -gemAmetrine -oreBlackDiamond -gemBlackDiamond -oreAlexandrite -gemAlexandrite -oreCoral -gemCoral -oreSunstone -gemSunstone -oreCatsEye -gemCatsEye -oreZircon -gemZircon -oreJade -gemJade -oreChrysoprase -gemChrysoprase -gemKyanite -oreAmmolite -gemAmmolite -oreKunzite -gemKunzite -oreRoseQuartz -gemRoseQuartz -oreTektite -gemTektite -orePearl -oreChaos -gemChaos -oreEnderEssence -gemEnderEssence -dustVoid -orePoorIron -orePoorGold -orePoorCopper -orePoorTin -orePoorLead -orePoorSilver -orePoorNickel -orePoorZinc From 08dc3b232df15e9b2a02b364761ea8d54bd73282 Mon Sep 17 00:00:00 2001 From: Alessian Dormal Date: Tue, 23 Dec 2025 23:44:49 +0100 Subject: [PATCH 18/18] I'll nuke this entire codebase and rewrite it anyways. Just need to be able to pull in the next few days. --- .prettierrc | 11 + eslint.config.mts | 63 + fsconfig.json | 0 package-lock.json | 4806 +++++++++++++------ package.json | 81 +- src/Events.ts | 75 + src/Server.ts | 52 + src/bot/EventHandler.ts | 94 +- src/clients/DiscordClient.ts | 61 +- src/clients/GrokClient.ts | 40 +- src/clients/RedisClient.ts | 72 +- src/config/config.ts | 14 +- src/config/fileSchema.ts | 80 +- src/config/schema.ts | 77 +- src/config/validate.ts | 57 +- src/controllers/AiController.ts | 35 +- src/controllers/MembersController.ts | 59 +- src/controllers/OredicController.ts | 93 +- src/core/Core.ts | 37 + src/core/Dependencies.ts | 7 + src/core/Timer.ts | 52 + src/db/DbHandler.ts | 270 +- src/discord.ts | 16 - src/express.ts | 34 - src/helpers/Logger.ts | 173 + src/helpers/Timer.ts | 84 + src/loaders/files.ts | 9 +- src/loaders/filters.ts | 31 +- src/loaders/keys.ts | 7 +- src/loaders/storage.ts | 25 +- src/middleware/analytics/perf/timer.ts | 26 +- src/middleware/auth/globalRateLimits.ts | 69 +- src/middleware/auth/leveretAuth.ts | 116 +- src/middleware/auth/userRateLimits.ts | 80 +- src/middleware/endpoints/aiEndpoint.ts | 78 +- src/middleware/endpoints/loadEndpoint.ts | 14 +- src/middleware/endpoints/membersEndpoint.ts | 11 +- src/middleware/endpoints/oredicEndpoint.ts | 62 +- src/middleware/sanity/aiTokenChecker.ts | 105 +- src/middleware/sanity/cleanup.ts | 34 +- src/middleware/sanity/filterBody.ts | 127 +- src/middleware/sanity/jsonWithRawBody.ts | 10 +- src/middleware/sanity/validator.ts | 44 +- src/routes/ai.ts | 49 +- src/routes/index.ts | 20 +- src/routes/members.ts | 19 +- src/routes/oredic.ts | 19 +- src/server.ts | 54 - src/services/ai/GrokService.ts | 49 +- src/services/members/MembersService.ts | 42 +- src/services/oredic/OredicService.ts | 145 +- src/services/rateLimits.ts | 131 +- src/types/ai.ts | 25 +- src/types/config.ts | 3 + src/types/errors.ts | 20 +- src/types/express.ts | 102 +- src/types/parsing.ts | 156 +- src/types/server.ts | 23 +- src/types/time/timer.ts | 7 + src/types/timer.ts | 6 +- src/utils/Logger.ts | 202 - src/utils/StringUtils.ts | 71 + src/utils/Timer.ts | 93 - src/utils/bot/usernames.ts | 14 +- src/utils/grok/formatter.ts | 82 +- src/utils/grok/grok.ts | 70 +- src/utils/parentClasses/ErrorProne.ts | 158 +- src/utils/parsers/OredicBuilder.ts | 259 +- src/utils/parsers/OredicMatcher.ts | 602 ++- src/utils/parsers/OredicParser.ts | 978 ++-- src/utils/parsers/OredicShortener.ts | 291 +- storage/filters/drugs.json | 79 +- storage/filters/exploitation.json | 59 +- storage/filters/exploits.json | 38 +- storage/filters/fraud.json | 89 +- storage/filters/hacking.json | 99 +- storage/filters/poisons.json | 59 +- storage/filters/privacy.json | 59 +- storage/filters/self-harm.json | 69 +- storage/filters/violence.json | 59 +- storage/filters/weapons.json | 79 +- tsconfig.eslint.json | 8 + tsconfig.json | 19 +- 83 files changed, 6601 insertions(+), 5096 deletions(-) create mode 100644 .prettierrc create mode 100644 eslint.config.mts create mode 100644 fsconfig.json create mode 100644 src/Events.ts create mode 100644 src/Server.ts create mode 100644 src/core/Core.ts create mode 100644 src/core/Dependencies.ts create mode 100644 src/core/Timer.ts delete mode 100644 src/discord.ts delete mode 100644 src/express.ts create mode 100644 src/helpers/Logger.ts create mode 100644 src/helpers/Timer.ts delete mode 100644 src/server.ts create mode 100644 src/types/config.ts create mode 100644 src/types/time/timer.ts delete mode 100644 src/utils/Logger.ts create mode 100644 src/utils/StringUtils.ts delete mode 100644 src/utils/Timer.ts create mode 100644 tsconfig.eslint.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..4755ecc --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "printWidth": 110, + "tabWidth": 4, + "useTabs": false, + "singleQuote": false, + "semi": true, + "trailingComma": "all", + "quoteProps": "consistent", + "arrowParens": "always", + "checkIgnorePragma": true +} diff --git a/eslint.config.mts b/eslint.config.mts new file mode 100644 index 0000000..5adb7a0 --- /dev/null +++ b/eslint.config.mts @@ -0,0 +1,63 @@ +import eslint from "@eslint/js"; +import { defineConfig } from "eslint/config"; +import nodePlugin from "eslint-plugin-n"; +import simpleImportSort from "eslint-plugin-simple-import-sort"; +import sonarJs from "eslint-plugin-sonarjs"; +import unusedImports from "eslint-plugin-unused-imports"; +import globals from "globals"; +import tseslint from "typescript-eslint"; + +export default defineConfig( + { + ignores: ["dist/**", "config/**"], + }, + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + languageOptions: { + globals: { + ...globals.node, + ...globals.es2021, + }, + parserOptions: { + project: "./tsconfig.eslint.json", + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + plugins: { + "unused-imports": unusedImports, + "n": nodePlugin, + "sonarjs": sonarJs, + "importsort": simpleImportSort, + }, + rules: { + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-floating-promises": "error", + "@typescript-eslint/no-misused-promises": "error", + "@typescript-eslint/await-thenable": "error", + "@typescript-eslint/return-await": "error", + "@typescript-eslint/strict-boolean-expressions": "warn", + "@typescript-eslint/switch-exhaustiveness-check": "error", + "@typescript-eslint/consistent-type-imports": "error", + "no-return-await": "off", + "unused-imports/no-unused-imports": "error", + "no-console": "error", + "eqeqeq": ["error", "always"], + "@typescript-eslint/no-explicit-any": "warn", + "no-duplicate-imports": "error", + "n/no-deprecated-api": "warn", + "n/prefer-global/process": "warn", + "sonarjs/no-duplicate-string": "warn", + "sonarjs/no-identical-functions": "warn", + "sonarjs/cognitive-complexity": ["warn", 15], + "no-param-reassign": "warn", + "no-implicit-coercion": "warn", + "no-warning-comments": ["warn", { terms: ["todo", "fixme"], location: "start" }], + "importsort/imports": "error", + "importsort/exports": "error", + }, + }, +); diff --git a/fsconfig.json b/fsconfig.json new file mode 100644 index 0000000..e69de29 diff --git a/package-lock.json b/package-lock.json index 2436238..889217b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1596 +1,3218 @@ { - "name": "grok-server", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "grok-server", - "version": "0.1.0", - "license": "MIT", - "dependencies": { - "discord.js": "^14.23.2", - "dotenv": "^17.2.3", - "express": "^5.1.0", - "fs": "^0.0.1-security", - "openai": "^6.3.0", - "redis": "^5.8.3", - "ts-node": "^10.9.2", - "typescript": "^5.9.3", - "zod": "^4.1.12" - }, - "devDependencies": { - "@types/express": "^5.0.3", - "ts-node": "^10.9.2", - "typescript": "^5.9.3" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@discordjs/builders": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.12.2.tgz", - "integrity": "sha512-AugKmrgRJOHXEyMkABH/hXpAmIBKbYokCEl9VAM4Kh6FvkdobQ+Zhv7AR6K+y5hS7+VQ7gKXPYCe1JQmV07H1g==", - "license": "Apache-2.0", - "dependencies": { - "@discordjs/formatters": "^0.6.1", - "@discordjs/util": "^1.1.1", - "@sapphire/shapeshift": "^4.0.0", - "discord-api-types": "^0.38.26", - "fast-deep-equal": "^3.1.3", - "ts-mixer": "^6.0.4", - "tslib": "^2.6.3" - }, - "engines": { - "node": ">=16.11.0" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/collection": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", - "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=16.11.0" - } - }, - "node_modules/@discordjs/formatters": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.1.tgz", - "integrity": "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg==", - "license": "Apache-2.0", - "dependencies": { - "discord-api-types": "^0.38.1" - }, - "engines": { - "node": ">=16.11.0" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/rest": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.6.0.tgz", - "integrity": "sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w==", - "license": "Apache-2.0", - "dependencies": { - "@discordjs/collection": "^2.1.1", - "@discordjs/util": "^1.1.1", - "@sapphire/async-queue": "^1.5.3", - "@sapphire/snowflake": "^3.5.3", - "@vladfrangu/async_event_emitter": "^2.4.6", - "discord-api-types": "^0.38.16", - "magic-bytes.js": "^1.10.0", - "tslib": "^2.6.3", - "undici": "6.21.3" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", - "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", - "license": "Apache-2.0", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/util": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz", - "integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==", - "license": "Apache-2.0", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/ws": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.3.tgz", - "integrity": "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==", - "license": "Apache-2.0", - "dependencies": { - "@discordjs/collection": "^2.1.0", - "@discordjs/rest": "^2.5.1", - "@discordjs/util": "^1.1.0", - "@sapphire/async-queue": "^1.5.2", - "@types/ws": "^8.5.10", - "@vladfrangu/async_event_emitter": "^2.2.4", - "discord-api-types": "^0.38.1", - "tslib": "^2.6.2", - "ws": "^8.17.0" - }, - "engines": { - "node": ">=16.11.0" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", - "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", - "license": "Apache-2.0", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@redis/bloom": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.3.tgz", - "integrity": "sha512-1eldTzHvdW3Oi0TReb8m1yiFt8ZwyF6rv1NpZyG5R4TpCwuAdKQetBKoCw7D96tNFgsVVd6eL+NaGZZCqhRg4g==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.3" - } - }, - "node_modules/@redis/client": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.3.tgz", - "integrity": "sha512-MZVUE+l7LmMIYlIjubPosruJ9ltSLGFmJqsXApTqPLyHLjsJUSAbAJb/A3N34fEqean4ddiDkdWzNu4ZKPvRUg==", - "license": "MIT", - "peer": true, - "dependencies": { - "cluster-key-slot": "1.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@redis/json": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.3.tgz", - "integrity": "sha512-DRR09fy/u8gynHGJ4gzXYeM7D8nlS6EMv5o+h20ndTJiAc7RGR01fdk2FNjnn1Nz5PjgGGownF+s72bYG4nZKQ==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.3" - } - }, - "node_modules/@redis/search": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.3.tgz", - "integrity": "sha512-EMIvEeGRR2I0BJEz4PV88DyCuPmMT1rDtznlsHY3cKSDcc9vj0Q411jUnX0iU2vVowUgWn/cpySKjpXdZ8m+5g==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.3" - } - }, - "node_modules/@redis/time-series": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.3.tgz", - "integrity": "sha512-5Jwy3ilsUYQjzpE7WZ1lEeG1RkqQ5kHtwV1p8yxXHSEmyUbC/T/AVgyjMcm52Olj/Ov/mhDKjx6ndYUi14bXsw==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.3" - } - }, - "node_modules/@sapphire/async-queue": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz", - "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==", - "license": "MIT", - "engines": { - "node": ">=v14.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/@sapphire/shapeshift": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz", - "integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=v16" - } - }, - "node_modules/@sapphire/snowflake": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz", - "integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==", - "license": "MIT", - "engines": { - "node": ">=v14.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", - "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", - "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.7.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz", - "integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==", - "license": "MIT", - "peer": true, - "dependencies": { - "undici-types": "~7.14.0" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.0.tgz", - "integrity": "sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.9.tgz", - "integrity": "sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "<1" - } - }, - "node_modules/@types/serve-static/node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@vladfrangu/async_event_emitter": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.7.tgz", - "integrity": "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==", - "license": "MIT", - "engines": { - "node": ">=v14.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/discord-api-types": { - "version": "0.38.30", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.30.tgz", - "integrity": "sha512-KhAqlBrg+rVK+Ob7INMF5o63yW4/GUzRatG/AjyVsIO8lgcLyR8qCl2HokIVzWwmzkJYG0CEPXsKMOqau3E8NA==", - "license": "MIT", - "workspaces": [ - "scripts/actions/documentation" - ] - }, - "node_modules/discord.js": { - "version": "14.23.2", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.23.2.tgz", - "integrity": "sha512-tU2NFr823X3TXEc8KyR/4m296KLxPai4nirN3q9kHCpY4TKj96n9lHZnyLzRNMui8EbL07jg9hgH2PWWfKMGIg==", - "license": "Apache-2.0", - "dependencies": { - "@discordjs/builders": "^1.12.1", - "@discordjs/collection": "1.5.3", - "@discordjs/formatters": "^0.6.1", - "@discordjs/rest": "^2.6.0", - "@discordjs/util": "^1.1.1", - "@discordjs/ws": "^1.2.3", - "@sapphire/snowflake": "3.5.3", - "discord-api-types": "^0.38.29", - "fast-deep-equal": "3.1.3", - "lodash.snakecase": "4.1.1", - "magic-bytes.js": "^1.10.0", - "tslib": "^2.6.3", - "undici": "6.21.3" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/dotenv": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", - "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fs": { - "version": "0.0.1-security", - "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", - "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==", - "license": "ISC" - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "license": "MIT" - }, - "node_modules/magic-bytes.js": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.12.1.tgz", - "integrity": "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA==", - "license": "MIT" - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/openai": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.3.0.tgz", - "integrity": "sha512-E6vOGtZvdcb4yXQ5jXvDlUG599OhIkb/GjBLZXS+qk0HF+PJReIldEc9hM8Ft81vn+N6dRdFRb7BZNK8bbvXrw==", - "license": "Apache-2.0", - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", - "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.7.0", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/redis": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/redis/-/redis-5.8.3.tgz", - "integrity": "sha512-MfSrfV6+tEfTw8c4W0yFp6XWX8Il4laGU7Bx4kvW4uiYM1AuZ3KGqEGt1LdQHeD1nEyLpIWetZ/SpY3kkbgrYw==", - "license": "MIT", - "dependencies": { - "@redis/bloom": "5.8.3", - "@redis/client": "5.8.3", - "@redis/json": "5.8.3", - "@redis/search": "5.8.3", - "@redis/time-series": "5.8.3" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/ts-mixer": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", - "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==", - "license": "MIT" - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici": { - "version": "6.21.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", - "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, - "node_modules/undici-types": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", - "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true + "name": "hog-api", + "version": "0.3.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hog-api", + "version": "0.3.0", + "license": "MIT", + "dependencies": { + "discord.js": "^14.23.2", + "dotenv": "^17.2.3", + "eslint": "^9.39.2", + "eslint-plugin-n": "^17.23.1", + "eslint-plugin-simple-import-sort": "^12.1.1", + "eslint-plugin-sonarjs": "^3.0.5", + "eslint-plugin-unused-imports": "^4.3.0", + "express": "^5.1.0", + "fs": "^0.0.1-security", + "jiti": "^2.6.1", + "openai": "^6.3.0", + "prettier": "^3.7.4", + "redis": "^5.8.3", + "ts-node": "^10.9.2", + "typescript": "^5.9.3", + "typescript-eslint": "^8.50.1", + "zod": "^4.1.12" + }, + "devDependencies": { + "@types/express": "^5.0.3", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@discordjs/builders": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.12.2.tgz", + "integrity": "sha512-AugKmrgRJOHXEyMkABH/hXpAmIBKbYokCEl9VAM4Kh6FvkdobQ+Zhv7AR6K+y5hS7+VQ7gKXPYCe1JQmV07H1g==", + "license": "Apache-2.0", + "dependencies": { + "@discordjs/formatters": "^0.6.1", + "@discordjs/util": "^1.1.1", + "@sapphire/shapeshift": "^4.0.0", + "discord-api-types": "^0.38.26", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.4", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=16.11.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/collection": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", + "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/formatters": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.1.tgz", + "integrity": "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg==", + "license": "Apache-2.0", + "dependencies": { + "discord-api-types": "^0.38.1" + }, + "engines": { + "node": ">=16.11.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/rest": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.6.0.tgz", + "integrity": "sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w==", + "license": "Apache-2.0", + "dependencies": { + "@discordjs/collection": "^2.1.1", + "@discordjs/util": "^1.1.1", + "@sapphire/async-queue": "^1.5.3", + "@sapphire/snowflake": "^3.5.3", + "@vladfrangu/async_event_emitter": "^2.4.6", + "discord-api-types": "^0.38.16", + "magic-bytes.js": "^1.10.0", + "tslib": "^2.6.3", + "undici": "6.21.3" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", + "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/util": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz", + "integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/ws": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.3.tgz", + "integrity": "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==", + "license": "Apache-2.0", + "dependencies": { + "@discordjs/collection": "^2.1.0", + "@discordjs/rest": "^2.5.1", + "@discordjs/util": "^1.1.0", + "@sapphire/async-queue": "^1.5.2", + "@types/ws": "^8.5.10", + "@vladfrangu/async_event_emitter": "^2.2.4", + "discord-api-types": "^0.38.1", + "tslib": "^2.6.2", + "ws": "^8.17.0" + }, + "engines": { + "node": ">=16.11.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", + "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@redis/bloom": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.3.tgz", + "integrity": "sha512-1eldTzHvdW3Oi0TReb8m1yiFt8ZwyF6rv1NpZyG5R4TpCwuAdKQetBKoCw7D96tNFgsVVd6eL+NaGZZCqhRg4g==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.3" + } + }, + "node_modules/@redis/client": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.3.tgz", + "integrity": "sha512-MZVUE+l7LmMIYlIjubPosruJ9ltSLGFmJqsXApTqPLyHLjsJUSAbAJb/A3N34fEqean4ddiDkdWzNu4ZKPvRUg==", + "license": "MIT", + "peer": true, + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@redis/json": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.3.tgz", + "integrity": "sha512-DRR09fy/u8gynHGJ4gzXYeM7D8nlS6EMv5o+h20ndTJiAc7RGR01fdk2FNjnn1Nz5PjgGGownF+s72bYG4nZKQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.3" + } + }, + "node_modules/@redis/search": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.3.tgz", + "integrity": "sha512-EMIvEeGRR2I0BJEz4PV88DyCuPmMT1rDtznlsHY3cKSDcc9vj0Q411jUnX0iU2vVowUgWn/cpySKjpXdZ8m+5g==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.3" + } + }, + "node_modules/@redis/time-series": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.3.tgz", + "integrity": "sha512-5Jwy3ilsUYQjzpE7WZ1lEeG1RkqQ5kHtwV1p8yxXHSEmyUbC/T/AVgyjMcm52Olj/Ov/mhDKjx6ndYUi14bXsw==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.3" + } + }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz", + "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==", + "license": "MIT", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/shapeshift": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz", + "integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v16" + } + }, + "node_modules/@sapphire/snowflake": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz", + "integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==", + "license": "MIT", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.7.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz", + "integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.14.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.0.tgz", + "integrity": "sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.9.tgz", + "integrity": "sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.50.1.tgz", + "integrity": "sha512-PKhLGDq3JAg0Jk/aK890knnqduuI/Qj+udH7wCf0217IGi4gt+acgCyPVe79qoT+qKUvHMDQkwJeKW9fwl8Cyw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.50.1", + "@typescript-eslint/type-utils": "8.50.1", + "@typescript-eslint/utils": "8.50.1", + "@typescript-eslint/visitor-keys": "8.50.1", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.50.1", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.50.1.tgz", + "integrity": "sha512-hM5faZwg7aVNa819m/5r7D0h0c9yC4DUlWAOvHAtISdFTc8xB86VmX5Xqabrama3wIPJ/q9RbGS1worb6JfnMg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.50.1", + "@typescript-eslint/types": "8.50.1", + "@typescript-eslint/typescript-estree": "8.50.1", + "@typescript-eslint/visitor-keys": "8.50.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.1.tgz", + "integrity": "sha512-E1ur1MCVf+YiP89+o4Les/oBAVzmSbeRB0MQLfSlYtbWU17HPxZ6Bhs5iYmKZRALvEuBoXIZMOIRRc/P++Ortg==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.50.1", + "@typescript-eslint/types": "^8.50.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.1.tgz", + "integrity": "sha512-mfRx06Myt3T4vuoHaKi8ZWNTPdzKPNBhiblze5N50//TSHOAQQevl/aolqA/BcqqbJ88GUnLqjjcBc8EWdBcVw==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.50.1", + "@typescript-eslint/visitor-keys": "8.50.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.1.tgz", + "integrity": "sha512-ooHmotT/lCWLXi55G4mvaUF60aJa012QzvLK0Y+Mp4WdSt17QhMhWOaBWeGTFVkb2gDgBe19Cxy1elPXylslDw==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.50.1.tgz", + "integrity": "sha512-7J3bf022QZE42tYMO6SL+6lTPKFk/WphhRPe9Tw/el+cEwzLz1Jjz2PX3GtGQVxooLDKeMVmMt7fWpYRdG5Etg==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.50.1", + "@typescript-eslint/typescript-estree": "8.50.1", + "@typescript-eslint/utils": "8.50.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.1.tgz", + "integrity": "sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.1.tgz", + "integrity": "sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.50.1", + "@typescript-eslint/tsconfig-utils": "8.50.1", + "@typescript-eslint/types": "8.50.1", + "@typescript-eslint/visitor-keys": "8.50.1", + "debug": "^4.3.4", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.50.1.tgz", + "integrity": "sha512-lCLp8H1T9T7gPbEuJSnHwnSuO9mDf8mfK/Nion5mZmiEaQD9sWf9W4dfeFqRyqRjF06/kBuTmAqcs9sewM2NbQ==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.50.1", + "@typescript-eslint/types": "8.50.1", + "@typescript-eslint/typescript-estree": "8.50.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.1.tgz", + "integrity": "sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.50.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vladfrangu/async_event_emitter": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.7.tgz", + "integrity": "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==", + "license": "MIT", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/discord-api-types": { + "version": "0.38.30", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.30.tgz", + "integrity": "sha512-KhAqlBrg+rVK+Ob7INMF5o63yW4/GUzRatG/AjyVsIO8lgcLyR8qCl2HokIVzWwmzkJYG0CEPXsKMOqau3E8NA==", + "license": "MIT", + "workspaces": [ + "scripts/actions/documentation" + ] + }, + "node_modules/discord.js": { + "version": "14.23.2", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.23.2.tgz", + "integrity": "sha512-tU2NFr823X3TXEc8KyR/4m296KLxPai4nirN3q9kHCpY4TKj96n9lHZnyLzRNMui8EbL07jg9hgH2PWWfKMGIg==", + "license": "Apache-2.0", + "dependencies": { + "@discordjs/builders": "^1.12.1", + "@discordjs/collection": "1.5.3", + "@discordjs/formatters": "^0.6.1", + "@discordjs/rest": "^2.6.0", + "@discordjs/util": "^1.1.1", + "@discordjs/ws": "^1.2.3", + "@sapphire/snowflake": "3.5.3", + "discord-api-types": "^0.38.29", + "fast-deep-equal": "3.1.3", + "lodash.snakecase": "4.1.1", + "magic-bytes.js": "^1.10.0", + "tslib": "^2.6.3", + "undici": "6.21.3" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.4", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "license": "MIT", + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-n": { + "version": "17.23.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.23.1.tgz", + "integrity": "sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.5.0", + "enhanced-resolve": "^5.17.1", + "eslint-plugin-es-x": "^7.8.0", + "get-tsconfig": "^4.8.1", + "globals": "^15.11.0", + "globrex": "^0.1.2", + "ignore": "^5.3.2", + "semver": "^7.6.3", + "ts-declaration-location": "^1.0.6" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": ">=8.23.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-simple-import-sort": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz", + "integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==", + "license": "MIT", + "peerDependencies": { + "eslint": ">=5.0.0" + } + }, + "node_modules/eslint-plugin-sonarjs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-3.0.5.tgz", + "integrity": "sha512-dI62Ff3zMezUToi161hs2i1HX1ie8Ia2hO0jtNBfdgRBicAG4ydy2WPt0rMTrAe3ZrlqhpAO3w1jcQEdneYoFA==", + "license": "LGPL-3.0-only", + "dependencies": { + "@eslint-community/regexpp": "4.12.1", + "builtin-modules": "3.3.0", + "bytes": "3.1.2", + "functional-red-black-tree": "1.0.1", + "jsx-ast-utils-x": "0.1.0", + "lodash.merge": "4.6.2", + "minimatch": "9.0.5", + "scslre": "0.3.0", + "semver": "7.7.2", + "typescript": ">=5" + }, + "peerDependencies": { + "eslint": "^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-sonarjs/node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/eslint-plugin-sonarjs/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/eslint-plugin-sonarjs/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/eslint-plugin-sonarjs/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-unused-imports": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.3.0.tgz", + "integrity": "sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==", + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", + "eslint": "^9.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "license": "ISC" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==", + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "license": "MIT" + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "peer": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "license": "MIT" + }, + "node_modules/jsx-ast-utils-x": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils-x/-/jsx-ast-utils-x-0.1.0.tgz", + "integrity": "sha512-eQQBjBnsVtGacsG9uJNB8qOr3yA8rga4wAaGG1qRcBzSIvfhERLrWxMAM1hp5fcS6Abo8M4+bUBTekYR0qTPQw==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "license": "MIT" + }, + "node_modules/magic-bytes.js": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.12.1.tgz", + "integrity": "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA==", + "license": "MIT" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openai": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.3.0.tgz", + "integrity": "sha512-E6vOGtZvdcb4yXQ5jXvDlUG599OhIkb/GjBLZXS+qk0HF+PJReIldEc9hM8Ft81vn+N6dRdFRb7BZNK8bbvXrw==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/redis": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/redis/-/redis-5.8.3.tgz", + "integrity": "sha512-MfSrfV6+tEfTw8c4W0yFp6XWX8Il4laGU7Bx4kvW4uiYM1AuZ3KGqEGt1LdQHeD1nEyLpIWetZ/SpY3kkbgrYw==", + "license": "MIT", + "dependencies": { + "@redis/bloom": "5.8.3", + "@redis/client": "5.8.3", + "@redis/json": "5.8.3", + "@redis/search": "5.8.3", + "@redis/time-series": "5.8.3" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/refa": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/refa/-/refa-0.12.1.tgz", + "integrity": "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==", + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/regexp-ast-analysis": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz", + "integrity": "sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==", + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.1" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scslre": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", + "integrity": "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==", + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.0", + "regexp-ast-analysis": "^0.7.0" + }, + "engines": { + "node": "^14.0.0 || >=16.0.0" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-declaration-location": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/ts-declaration-location/-/ts-declaration-location-1.0.7.tgz", + "integrity": "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==", + "funding": [ + { + "type": "ko-fi", + "url": "https://ko-fi.com/rebeccastevens" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/ts-declaration-location" + } + ], + "license": "BSD-3-Clause", + "dependencies": { + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": ">=4.0.0" + } + }, + "node_modules/ts-mixer": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==", + "license": "MIT" + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.50.1.tgz", + "integrity": "sha512-ytTHO+SoYSbhAH9CrYnMhiLx8To6PSSvqnvXyPUgPETCvB6eBKmTI9w6XMPS3HsBRGkwTVBX+urA8dYQx6bHfQ==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.50.1", + "@typescript-eslint/parser": "8.50.1", + "@typescript-eslint/typescript-estree": "8.50.1", + "@typescript-eslint/utils": "8.50.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici": { + "version": "6.21.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", + "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", + "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/zod": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", - "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } } - } } diff --git a/package.json b/package.json index 3103b5c..af0ab6c 100644 --- a/package.json +++ b/package.json @@ -1,36 +1,49 @@ { - "name": "grok-server", - "version": "0.2.0", - "description": "just a simple express api for running grok on leveret", - "main": "dist/server.js", - "scripts": { - "build": "tsc", - "dev": "ts-node src/server.ts" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/D-Alessian/grok-server.git" - }, - "author": "D-Alessian", - "license": "MIT", - "bugs": { - "url": "https://github.com/D-Alessian/grok-server/issues" - }, - "homepage": "https://github.com/D-Alessian/grok-server#readme", - "dependencies": { - "discord.js": "^14.23.2", - "dotenv": "^17.2.3", - "express": "^5.1.0", - "fs": "^0.0.1-security", - "openai": "^6.3.0", - "redis": "^5.8.3", - "ts-node": "^10.9.2", - "typescript": "^5.9.3", - "zod": "^4.1.12" - }, - "devDependencies": { - "@types/express": "^5.0.3", - "ts-node": "^10.9.2", - "typescript": "^5.9.3" - } + "name": "hog-api", + "version": "0.3.0", + "description": "just a simple express api for running grok on leveret", + "main": "dist/Server.js", + "scripts": { + "build": "tsc", + "dev": "ts-node src/Server.ts", + "format": "prettier --write .", + "format:check": "prettier --check .", + "lint:check": "eslint .", + "lint:fix": "eslint . --fix", + "lint:report": "eslint \"src/**/*.ts\" --format json --output-file temp/lint/eslint_report.json" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/D-Alessian/grok-server.git" + }, + "author": "D-Alessian", + "license": "MIT", + "bugs": { + "url": "https://github.com/D-Alessian/grok-server/issues" + }, + "homepage": "https://github.com/D-Alessian/grok-server#readme", + "dependencies": { + "discord.js": "^14.23.2", + "dotenv": "^17.2.3", + "eslint": "^9.39.2", + "eslint-plugin-n": "^17.23.1", + "eslint-plugin-simple-import-sort": "^12.1.1", + "eslint-plugin-sonarjs": "^3.0.5", + "eslint-plugin-unused-imports": "^4.3.0", + "express": "^5.1.0", + "fs": "^0.0.1-security", + "jiti": "^2.6.1", + "openai": "^6.3.0", + "prettier": "^3.7.4", + "redis": "^5.8.3", + "ts-node": "^10.9.2", + "typescript": "^5.9.3", + "typescript-eslint": "^8.50.1", + "zod": "^4.1.12" + }, + "devDependencies": { + "@types/express": "^5.0.3", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" + } } diff --git a/src/Events.ts b/src/Events.ts new file mode 100644 index 0000000..6af0b87 --- /dev/null +++ b/src/Events.ts @@ -0,0 +1,75 @@ +import express from "express"; +import fs from "fs"; +import path from "path"; + +import { BotEventHandler } from "./bot/EventHandler"; +import { getDiscordClient, initDiscordClient } from "./clients/DiscordClient"; +import { initGrokClient } from "./clients/GrokClient"; +import { getRedisClient, initRedisClient } from "./clients/RedisClient"; +import { getDbHandler, initDbHandler } from "./db/DbHandler"; +import { initFs } from "./helpers/files/FileSystem"; +import { getLogger, initLogger } from "./helpers/Logger"; +import { startTimer } from "./helpers/Timer"; +import { routes } from "./routes"; +import { startGlobalRateLimitIncrement, startUserRateLimitIncrements } from "./services/rateLimits"; +import { ErrorProne } from "./utils/parentClasses/ErrorProne"; + +export class Events extends ErrorProne { + constructor() { + super(); + } + + static initEssentials() { + startTimer("main"); + initLogger(); + initFs(JSON.parse(fs.readFileSync(path.join(process.cwd(), "fsconfig.json"), "utf-8"))); + } + + static async initDb() { + initDbHandler(); + await getDbHandler().init(); + } + + static async initClients() { + initRedisClient(); + await getRedisClient().connect(); + initGrokClient(); + } + + static async initServices() { + startGlobalRateLimitIncrement(); + await startUserRateLimitIncrements(); + } + + static async initDiscord() { + initDiscordClient(); + const client = getDiscordClient().client; + const eventHandler = new BotEventHandler(); + + client.once("clientReady", () => eventHandler.onReady()); + client.on("guildMemberAdd", (member) => eventHandler.onNewMember(member)); + client.on("guildMemberRemove", (member) => eventHandler.onRemoveMember(member)); + + await getDiscordClient().connect(); + } + + static async initExpress() { + const app = express(); + + app.use(routes()); + + app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => { + getLogger().simpleLog("error", `Unhandled error: ${err.message}`); + console.error(err.stack); + + if (!res.headersSent) { + res.status(500).json({ + error: "Internal Server Error", + message: process.env.NODE_ENV === "development" ? err.message : "Something went wrong", + }); + } + }); + + return app; + } +} diff --git a/src/Server.ts b/src/Server.ts new file mode 100644 index 0000000..ec0487f --- /dev/null +++ b/src/Server.ts @@ -0,0 +1,52 @@ +import type { Express } from "express"; + +import { config } from "./config/config"; +import { Events } from "./Events"; +import { getLogger } from "./helpers/Logger"; +import { stopTimer } from "./helpers/Timer"; +import { ErrorProne } from "./utils/parentClasses/ErrorProne"; + +class Server extends ErrorProne { + app: Express | null; + + constructor() { + super(); + this.app = null; + } + + async main() { + Events.initEssentials(); + + getLogger().formattingLog("Clients Init"); + await Events.initClients(); + + getLogger().formattingLog("DB Init"); + await Events.initDb(); + + getLogger().formattingLog("Discord Bot Init"); + await Events.initDiscord(); + + getLogger().formattingLog("Services Init"); + await Events.initServices(); + + getLogger().formattingLog("App Init"); + this.app = await Events.initExpress(); + } +} + +const server = new Server(); +server.main(); + +if (!server.app) { + throw new Error("Critical Error: Could not get express app"); +} + +server.app.listen(config.PORT, config.RUNNING_IP, () => { + getLogger().formattingLog("Server Info"); + getLogger().simpleLog("info", `Server is running at http://${config.RUNNING_IP}:${config.PORT}`); + getLogger().simpleLog( + "telemetry", + `Server took ${stopTimer("main").getTime("auto", 3).formatted} to start`, + ); + getLogger().formattingLog("Server Ready"); +}); diff --git a/src/bot/EventHandler.ts b/src/bot/EventHandler.ts index 6d60ca0..5839f6e 100644 --- a/src/bot/EventHandler.ts +++ b/src/bot/EventHandler.ts @@ -1,67 +1,63 @@ -import { GuildMember, PartialGuildMember, Guild } from "discord.js"; +import type { Guild, GuildMember, PartialGuildMember } from "discord.js"; + import { getDiscordClient } from "../clients/DiscordClient"; -import { getLogger } from "../utils/Logger"; import { config, env } from "../config/config"; import { getDbHandler } from "../db/DbHandler"; +import { getLogger } from "../helpers/Logger"; import { findDcUsernameById } from "../utils/bot/usernames"; export class BotEventHandler { - client; - guild: Guild | null = null; - dbHandler; + client; + guild: Guild | null = null; + dbHandler; + + constructor() { + this.client = getDiscordClient().client; + this.dbHandler = getDbHandler(); + } - constructor() { - this.client = getDiscordClient().client; - this.dbHandler = getDbHandler(); - } + private async ensureGuild(): Promise { + if (this.guild) return this.guild; - private async ensureGuild(): Promise { - if (this.guild) return this.guild; + const guild = this.client.guilds.cache.get(env.SERVER_ID); + if (!guild) { + throw new Error(`Failed to fetch Server ID: ${env.SERVER_ID}`); + } - const guild = this.client.guilds.cache.get(env.SERVER_ID); - if (!guild) { - throw new Error(`Failed to fetch Server ID: ${env.SERVER_ID}`); + this.guild = guild; + return guild; } - this.guild = guild; - return guild; - } - - async onReady() { - const guild = await this.ensureGuild(); + async onReady() { + const guild = await this.ensureGuild(); - getLogger().simpleLog( - "info", - `Discord Bot ready as ${this.client.user?.tag}` - ); + getLogger().simpleLog("info", `Discord Bot ready as ${this.client.user?.tag}`); - const members = await guild.members.fetch(); - let memberIds: string[] = []; + const members = await guild.members.fetch(); + const memberIds: string[] = []; - members.forEach((value, key) => { - memberIds.push(key); - }); + members.forEach((value, key) => { + memberIds.push(key); + }); - this.dbHandler.createHog(memberIds); - } + this.dbHandler.createHog(memberIds); + } - async onNewMember(member: GuildMember | PartialGuildMember) { - const memberId = member.id; - this.dbHandler.addToHog(memberId); - getLogger().simpleLog( - "telemetry", - `${await findDcUsernameById(memberId)} joined ${ - config.DISCORD_SERVER_NAME - }` - ); - } + async onNewMember(member: GuildMember | PartialGuildMember) { + const memberId = member.id; + this.dbHandler.addToHog(memberId); + getLogger().simpleLog( + "telemetry", + `${await findDcUsernameById(memberId)} joined ${config.DISCORD_SERVER_NAME}`, + ); + } - async onRemoveMember(member: GuildMember | PartialGuildMember) { - const memberId = member.id; - this.dbHandler.removeFromHog(memberId); - getLogger().simpleLog( - "warn", - `${await findDcUsernameById(memberId)} left ${config.DISCORD_SERVER_NAME}` - ); - } + async onRemoveMember(member: GuildMember | PartialGuildMember) { + const memberId = member.id; + this.dbHandler.removeFromHog(memberId); + getLogger().simpleLog( + "warn", + `${await findDcUsernameById(memberId)} left ${config.DISCORD_SERVER_NAME}`, + ); + } } diff --git a/src/clients/DiscordClient.ts b/src/clients/DiscordClient.ts index f55c37d..27f9ebf 100644 --- a/src/clients/DiscordClient.ts +++ b/src/clients/DiscordClient.ts @@ -1,47 +1,42 @@ import { Client, GatewayIntentBits } from "discord.js"; -import { getLogger } from "../utils/Logger"; + import { env } from "../config/config"; +import { getLogger } from "../helpers/Logger"; let discordClient: DiscordClient | null = null; export class DiscordClient { - client: Client; - - constructor() { - this.client = this.buildClient(); - } - - static gatewayIntents = [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMembers, - ]; - - buildClient() { - return new Client({ - intents: DiscordClient.gatewayIntents, - }); - } - - async connect() { - try { - await this.client.login(env.DISCORD_BOT_TOKEN); - getLogger().simpleLog("success", "Discord Client Connected Successfully"); - } catch (err) { - getLogger().simpleLog("error", `Failed to connect to Discord: ${err}`); + client: Client; + + constructor() { + this.client = this.buildClient(); + } + + static gatewayIntents = [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers]; + + buildClient() { + return new Client({ + intents: DiscordClient.gatewayIntents, + }); + } + + async connect() { + try { + await this.client.login(env.DISCORD_BOT_TOKEN); + getLogger().simpleLog("success", "Discord Client Connected Successfully"); + } catch (err) { + getLogger().simpleLog("error", `Failed to connect to Discord: ${err}`); + } } - } } export function initDiscordClient(): DiscordClient { - if (discordClient) return discordClient; - discordClient = new DiscordClient(); - return discordClient; + if (discordClient) return discordClient; + discordClient = new DiscordClient(); + return discordClient; } export function getDiscordClient(): DiscordClient { - if (!discordClient) - throw new Error( - "Discord Client not initialized. Call initDiscordClient() first." - ); - return discordClient; + if (!discordClient) throw new Error("Discord Client not initialized. Call initDiscordClient() first."); + return discordClient; } diff --git a/src/clients/GrokClient.ts b/src/clients/GrokClient.ts index 52e6b1b..a6ab0be 100644 --- a/src/clients/GrokClient.ts +++ b/src/clients/GrokClient.ts @@ -1,36 +1,34 @@ import OpenAI from "openai"; + import { config, env } from "../config/config"; -import { getLogger } from "../utils/Logger"; +import { getLogger } from "../helpers/Logger"; let grokClient: GrokClient | null = null; export class GrokClient { - client: OpenAI; + client: OpenAI; - constructor() { - this.client = this.buildClient(); - } + constructor() { + this.client = this.buildClient(); + } - buildClient() { - getLogger().simpleLog("success", "Building Grok Client"); - return new OpenAI({ - apiKey: env.GROK_API_KEY, - baseURL: "https://api.x.ai/v1", - timeout: config.GROK_TIMEOUT, - }); - } + buildClient() { + getLogger().simpleLog("success", "Building Grok Client"); + return new OpenAI({ + apiKey: env.GROK_API_KEY, + baseURL: "https://api.x.ai/v1", + timeout: config.GROK_TIMEOUT, + }); + } } export function initGrokClient(): GrokClient { - if (grokClient) return grokClient; - grokClient = new GrokClient(); - return grokClient; + if (grokClient) return grokClient; + grokClient = new GrokClient(); + return grokClient; } export function getGrokClient(): GrokClient { - if (!grokClient) - throw new Error( - "Grok Client not initialized. Call initGrokClient() first." - ); - return grokClient; + if (!grokClient) throw new Error("Grok Client not initialized. Call initGrokClient() first."); + return grokClient; } diff --git a/src/clients/RedisClient.ts b/src/clients/RedisClient.ts index c679c11..ba6b3c0 100644 --- a/src/clients/RedisClient.ts +++ b/src/clients/RedisClient.ts @@ -1,52 +1,48 @@ import { createClient } from "redis"; + import { env } from "../config/config"; -import { getLogger } from "../utils/Logger"; +import { getLogger } from "../helpers/Logger"; let redisClient: RedisClient | null = null; export class RedisClient { - client: ReturnType; - - constructor() { - this.client = this.buildClient(); - } - - buildClient() { - const client = createClient({ - username: env.REDIS_USERNAME, - password: env.REDIS_PASSWORD, - socket: { - host: env.REDIS_HOST, - port: env.REDIS_PORT, - tls: false, - }, - }); - client.on("error", (err) => - getLogger().simpleLog("error", `Redis Client Error ${err}`) - ); - return client; - } - - async connect() { - try { - await this.client.connect(); - getLogger().simpleLog("success", "Redis Client Connected Successfully"); - } catch (err) { - getLogger().simpleLog("error", `Failed to connect to Redis: ${err}`); + client: ReturnType; + + constructor() { + this.client = this.buildClient(); + } + + buildClient() { + const client = createClient({ + username: env.REDIS_USERNAME, + password: env.REDIS_PASSWORD, + socket: { + host: env.REDIS_HOST, + port: env.REDIS_PORT, + tls: false, + }, + }); + client.on("error", (err) => getLogger().simpleLog("error", `Redis Client Error ${err}`)); + return client; + } + + async connect() { + try { + await this.client.connect(); + getLogger().simpleLog("success", "Redis Client Connected Successfully"); + } catch (err) { + getLogger().simpleLog("error", `Failed to connect to Redis: ${err}`); + } } - } } export function initRedisClient(): RedisClient { - if (redisClient) return redisClient; - redisClient = new RedisClient(); - return redisClient; + if (redisClient) return redisClient; + redisClient = new RedisClient(); + return redisClient; } export function getRedisClient(): RedisClient { - if (!redisClient) - throw new Error( - "Redis Client not initialized. Call initRedisClient() first." - ); - return redisClient; + if (!redisClient) throw new Error("Redis Client not initialized. Call initRedisClient() first."); + return redisClient; } diff --git a/src/config/config.ts b/src/config/config.ts index 63d2c67..515ae0e 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -1,17 +1,11 @@ -import path from "path"; -import fs from "fs"; import dotenv from "dotenv"; -import { Config, Env, FiltersConfig } from "./schema"; -import { - validateConfigs, - validateEnvs, - validateFiltersConfigs, -} from "./validate"; + import { RAW_CONFIG, RAW_FILTERS_CONFIG } from "../loaders/storage"; +import type { Config, Env, FiltersConfig } from "./schema"; +import { validateConfigs, validateEnvs, validateFiltersConfigs } from "./validate"; dotenv.config({ quiet: true }); export const env: Env = validateEnvs(); export const config: Config = validateConfigs(RAW_CONFIG); -export const filtersConfig: FiltersConfig = - validateFiltersConfigs(RAW_FILTERS_CONFIG); +export const filtersConfig: FiltersConfig = validateFiltersConfigs(RAW_FILTERS_CONFIG); diff --git a/src/config/fileSchema.ts b/src/config/fileSchema.ts index 5e7f1f5..b6e3bc9 100644 --- a/src/config/fileSchema.ts +++ b/src/config/fileSchema.ts @@ -1,59 +1,59 @@ -import z, { base64, string } from "zod"; +import z, { string } from "zod"; export const EnvFileSchema = z.object({ - GROK_API_KEY: z.string().min(1), - DISCORD_BOT_TOKEN: z.string().min(1), - NODE_ENV: z - .enum(["development", "production", "test"]) - .default("development"), - LEVERET_PUB_KEY_B64: z.base64(), - REDIS_USERNAME: z.string(), - REDIS_PASSWORD: z - .string() - .min(8, "Password is too short. Change your DB's password."), - REDIS_HOST: z.string(), - REDIS_PORT: z.coerce.number().int().min(1).max(9999), - SERVER_ID: z.string(), + GROK_API_KEY: z.string().min(1), + DISCORD_BOT_TOKEN: z.string().min(1), + NODE_ENV: z.enum(["development", "production", "test"]).default("development"), + LEVERET_PUB_KEY_B64: z.base64(), + REDIS_USERNAME: z.string(), + REDIS_PASSWORD: z.string().min(8, "Password is too short. Change your DB's password."), + REDIS_HOST: z.string(), + REDIS_PORT: z.coerce.number().int().min(1).max(9999), + SERVER_ID: z.string(), }); const RateLimitConfigSchema = z.object({ - maxStored: z.number().int().positive(), - incrementInterval_s: z.number().int().positive(), - incrementAmount: z.number().int().min(1), + maxStored: z.number().int().positive(), + incrementInterval_s: z.number().int().positive(), + incrementAmount: z.number().int().min(1), }); const EndpointConfigSchema = z.object({ - model: z.string(), - maxPromptTokens: z.number().int().min(10).max(9999), - maxContextTokens: z.number().int().max(9999), - maxTotalTokens: z.number().int().min(10).max(19999), - filters: z.string(), // Reference to filters config key - rateLimit: z.object({ - global: RateLimitConfigSchema, - user: RateLimitConfigSchema.extend({ - whitelist: z.array(z.string().min(18).max(19).optional()), + model: z.string(), + maxPromptTokens: z.number().int().min(10).max(9999), + maxContextTokens: z.number().int().max(9999), + maxTotalTokens: z.number().int().min(10).max(19999), + filters: z.string(), // Reference to filters config key + rateLimit: z.object({ + global: RateLimitConfigSchema, + user: RateLimitConfigSchema.extend({ + whitelist: z.array(z.string().min(18).max(19).optional()), + }), }), - }), }); export const ConfigFileSchema = z.object({ - port: z.number().int().min(1).max(9999), - grokTimeout_ms: z.number().int().min(1000), - runningIp: z.ipv4(), - loggerName: z.string(), - discordServerName: z.string(), - skipLeveretAuth: z.boolean(), - acceptedTags: z.array(z.string().optional()), - endpoints: z.record(z.string(), EndpointConfigSchema), + port: z.number().int().min(1).max(9999), + grokTimeout_ms: z.number().int().min(1000), + runningIp: z.ipv4(), + loggerName: z.string(), + discordServerName: z.string(), + skipLeveretAuth: z.boolean(), + acceptedTags: z.array(z.string().optional()), + endpoints: z.record(z.string(), EndpointConfigSchema), }); const endpointSpecificFiltersConfigSchema = z.object({ - enabledFilters: z.array(string()), - filterAction: z.enum(["warn", "replace", "block"]), - logViolations: z.boolean(), - customMessage: z.string(), + enabledFilters: z.array(string()), + filterAction: z.enum(["warn", "replace", "block"]), + logViolations: z.boolean(), + customMessage: z.string(), }); export const FiltersConfigFileSchema = z.object({ - grok: endpointSpecificFiltersConfigSchema, + grok: endpointSpecificFiltersConfigSchema, }); + +export type EnvSchema = z.infer; +export type ConfigSchema = z.infer; +export type FiltersSchema = z.infer; diff --git a/src/config/schema.ts b/src/config/schema.ts index 9d12caa..bc28efd 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -1,48 +1,45 @@ -import z from "zod"; -import { - ConfigFileSchema, - EnvFileSchema, - FiltersConfigFileSchema, -} from "./fileSchema"; +import type z from "zod"; + +import { ConfigFileSchema, EnvFileSchema, FiltersConfigFileSchema } from "./fileSchema"; export const EnvSchema = EnvFileSchema; export const FilterConfigSchema = FiltersConfigFileSchema; export const ConfigSchema = ConfigFileSchema.transform((f) => ({ - PORT: f.port, - GROK_TIMEOUT: f.grokTimeout_ms, - RUNNING_IP: f.runningIp, - LOGGER_NAME: f.loggerName, - DISCORD_SERVER_NAME: f.discordServerName, - SKIP_LEVERET_AUTH: f.skipLeveretAuth, - ACCEPTED_TAGS: f.acceptedTags, - ENDPOINTS: Object.fromEntries( - Object.entries(f.endpoints).map(([key, endpoint]) => [ - key, - { - MODEL: endpoint.model, - MAX_PROMPT_TK: endpoint.maxPromptTokens, - MAX_CONTEXT_TK: endpoint.maxContextTokens, - MAX_TOTAL_TK: endpoint.maxTotalTokens, - FILTERS: endpoint.filters, - RATE_LIMIT: { - GLOBAL: { - MAX_STORED: endpoint.rateLimit.global.maxStored, - INTERVAL: endpoint.rateLimit.global.incrementInterval_s, - AMOUNT: endpoint.rateLimit.global.incrementAmount, - }, - USER: { - MAX_STORED: endpoint.rateLimit.user.maxStored, - INTERVAL: endpoint.rateLimit.user.incrementInterval_s, - AMOUNT: endpoint.rateLimit.user.incrementAmount, - WHITELIST: endpoint.rateLimit.user.whitelist.filter( - (id): id is string => Boolean(id) - ), - }, - }, - }, - ]) - ), + PORT: f.port, + GROK_TIMEOUT: f.grokTimeout_ms, + RUNNING_IP: f.runningIp, + LOGGER_NAME: f.loggerName, + DISCORD_SERVER_NAME: f.discordServerName, + SKIP_LEVERET_AUTH: f.skipLeveretAuth, + ACCEPTED_TAGS: f.acceptedTags, + ENDPOINTS: Object.fromEntries( + Object.entries(f.endpoints).map(([key, endpoint]) => [ + key, + { + MODEL: endpoint.model, + MAX_PROMPT_TK: endpoint.maxPromptTokens, + MAX_CONTEXT_TK: endpoint.maxContextTokens, + MAX_TOTAL_TK: endpoint.maxTotalTokens, + FILTERS: endpoint.filters, + RATE_LIMIT: { + GLOBAL: { + MAX_STORED: endpoint.rateLimit.global.maxStored, + INTERVAL: endpoint.rateLimit.global.incrementInterval_s, + AMOUNT: endpoint.rateLimit.global.incrementAmount, + }, + USER: { + MAX_STORED: endpoint.rateLimit.user.maxStored, + INTERVAL: endpoint.rateLimit.user.incrementInterval_s, + AMOUNT: endpoint.rateLimit.user.incrementAmount, + WHITELIST: endpoint.rateLimit.user.whitelist.filter((id): id is string => + Boolean(id), + ), + }, + }, + }, + ]), + ), })); export type Env = z.infer; diff --git a/src/config/validate.ts b/src/config/validate.ts index 080fab2..2b3aeef 100644 --- a/src/config/validate.ts +++ b/src/config/validate.ts @@ -1,42 +1,29 @@ -import { getLogger, Logger } from "../utils/Logger"; -import { - Config, - ConfigSchema, - Env, - EnvSchema, - FiltersConfig, - FilterConfigSchema, -} from "./schema"; +import type { Config, Env, FiltersConfig } from "./schema"; +import { ConfigSchema, EnvSchema, FilterConfigSchema } from "./schema"; export function validateEnvs(): Env { - const parsed = EnvSchema.safeParse(process.env); - if (!parsed.success) { - const errors = parsed.error.issues - .map((i) => `${i.path.join(".")}: ${i.message}`) - .join("; "); - throw new Error(`Env validation failed: ${errors}`); - } - return parsed.data; + const parsed = EnvSchema.safeParse(process.env); + if (!parsed.success) { + const errors = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; "); + throw new Error(`Env validation failed: ${errors}`); + } + return parsed.data; } -export function validateConfigs(json: Object): Config { - const parsed = ConfigSchema.safeParse(json); - if (!parsed.success) { - const errors = parsed.error.issues - .map((i) => `${i.path.join(".")}: ${i.message}`) - .join("; "); - throw new Error(`Config validation failed: ${errors}`); - } - return parsed.data; +export function validateConfigs(json: object): Config { + const parsed = ConfigSchema.safeParse(json); + if (!parsed.success) { + const errors = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; "); + throw new Error(`Config validation failed: ${errors}`); + } + return parsed.data; } -export function validateFiltersConfigs(json: Object): FiltersConfig { - const parsed = FilterConfigSchema.safeParse(json); - if (!parsed.success) { - const errors = parsed.error.issues - .map((i) => `${i.path.join(".")}: ${i.message}`) - .join("; "); - throw new Error(`Filter Config validation failed: ${errors}`); - } - return parsed.data; +export function validateFiltersConfigs(json: object): FiltersConfig { + const parsed = FilterConfigSchema.safeParse(json); + if (!parsed.success) { + const errors = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; "); + throw new Error(`Filter Config validation failed: ${errors}`); + } + return parsed.data; } diff --git a/src/controllers/AiController.ts b/src/controllers/AiController.ts index 77da215..88062af 100644 --- a/src/controllers/AiController.ts +++ b/src/controllers/AiController.ts @@ -1,27 +1,24 @@ -import { Request, Response, NextFunction } from "express"; +import type { NextFunction, Request, Response } from "express"; + import { GrokService } from "../services/ai/GrokService"; export class AiController { - private service = new GrokService(); + private service = new GrokService(); - prompt = async (req: Request, res: Response, next: NextFunction) => { - const { model, systemPrompt } = req.ai || {}; + prompt = async (req: Request, res: Response, next: NextFunction) => { + const { model, systemPrompt } = req.ai || {}; - if (!model || !systemPrompt) { - return next(new Error("AI model or system prompt is missing")); - } + if (!model || !systemPrompt) { + return next(new Error("AI model or system prompt is missing")); + } - try { - const completion = await this.service.generateCompletion( - req.body, - model, - systemPrompt - ); + try { + const completion = await this.service.generateCompletion(req.body, model, systemPrompt); - res.data = completion; - next(); - } catch (error) { - next(error); - } - }; + res.data = completion; + next(); + } catch (error) { + next(error); + } + }; } diff --git a/src/controllers/MembersController.ts b/src/controllers/MembersController.ts index cd20c9c..3e9756a 100644 --- a/src/controllers/MembersController.ts +++ b/src/controllers/MembersController.ts @@ -1,36 +1,37 @@ -import { Request, Response, NextFunction } from "express"; +import type { NextFunction, Request, Response } from "express"; + import { MembersService } from "../services/members/MembersService"; export class MembersController { - private service = new MembersService(); + private service = new MembersService(); - getIds = async (req: Request, res: Response, next: NextFunction) => { - try { - const members = await this.service.getIds(); - res.data = { ids: members }; - next(); - } catch (error) { - next(error); - } - }; + getIds = async (req: Request, res: Response, next: NextFunction) => { + try { + const members = await this.service.getIds(); + res.data = { ids: members }; + next(); + } catch (error) { + next(error); + } + }; - getUsernames = async (req: Request, res: Response, next: NextFunction) => { - try { - const members = await this.service.getUsernames(); - res.data = { usernames: members }; - next(); - } catch (error) { - next(error); - } - }; + getUsernames = async (req: Request, res: Response, next: NextFunction) => { + try { + const members = await this.service.getUsernames(); + res.data = { usernames: members }; + next(); + } catch (error) { + next(error); + } + }; - getUsers = async (req: Request, res: Response, next: NextFunction) => { - try { - const members = await this.service.getUsers(); - res.data = { users: members }; - next(); - } catch (error) { - next(error); - } - }; + getUsers = async (req: Request, res: Response, next: NextFunction) => { + try { + const members = await this.service.getUsers(); + res.data = { users: members }; + next(); + } catch (error) { + next(error); + } + }; } diff --git a/src/controllers/OredicController.ts b/src/controllers/OredicController.ts index 92c2191..907b439 100644 --- a/src/controllers/OredicController.ts +++ b/src/controllers/OredicController.ts @@ -1,54 +1,55 @@ -import { Request, Response, NextFunction } from "express"; +import type { NextFunction, Request, Response } from "express"; + +import type { OredicPack } from "../config/routes"; import { OredicService } from "../services/oredic/OredicService"; -import { assertOredicRequest, OredicRequest } from "../types/express"; -import { OredicPack } from "../config/routes"; +import { assertOredicRequest } from "../types/express"; import { ErrorProne } from "../utils/parentClasses/ErrorProne"; export class OredicController extends ErrorProne { - constructor() { - super(); - } - - parse = async (req: Request, res: Response, next: NextFunction) => { - assertOredicRequest(req); - const service = this.initializeService(req.oredic.pack); - const filter = req.body.filter; - - if (!filter) { - this.setError( - 500, - "Received no filter in OredicController. Should have been validated properly first." - ); - res.status(this.getErrorCode()).send(this.sendErrorToClient()); - return; + constructor() { + super(); } - const parsed = await service.parse(filter); - if (this.isError(parsed)) { - this.propagateError(parsed, "Could not parse", "OredicController"); - res.status(this.getErrorCode()).send(this.sendErrorToClient()); - return; + parse = async (req: Request, res: Response, next: NextFunction) => { + assertOredicRequest(req); + const service = this.initializeService(req.oredic.pack); + const filter = req.body.filter; + + if (!filter) { + this.setError( + 500, + "Received no filter in OredicController. Should have been validated properly first.", + ); + res.status(this.getErrorCode()).send(this.sendErrorToClient()); + return; + } + + const parsed = await service.parse(filter); + if (this.isError(parsed)) { + this.propagateError(parsed, "Could not parse", "OredicController"); + res.status(this.getErrorCode()).send(this.sendErrorToClient()); + return; + } + + res.data = { ast: parsed }; + }; + + build = async (req: Request, res: Response, next: NextFunction) => { + assertOredicRequest(req); + const service = this.initializeService(req.oredic.pack); + }; + + match = async (req: Request, res: Response, next: NextFunction) => { + assertOredicRequest(req); + const service = this.initializeService(req.oredic.pack); + }; + + simplify = async (req: Request, res: Response, next: NextFunction) => { + assertOredicRequest(req); + const service = this.initializeService(req.oredic.pack); + }; + + initializeService(pack: OredicPack) { + return new OredicService(pack); } - - res.data = { ast: parsed }; - }; - - build = async (req: Request, res: Response, next: NextFunction) => { - assertOredicRequest(req); - const service = this.initializeService(req.oredic.pack); - }; - - match = async (req: Request, res: Response, next: NextFunction) => { - assertOredicRequest(req); - const service = this.initializeService(req.oredic.pack); - }; - - simplify = async (req: Request, res: Response, next: NextFunction) => { - assertOredicRequest(req); - const service = this.initializeService(req.oredic.pack); - }; - - initializeService(pack: OredicPack) { - return new OredicService(pack); - } } diff --git a/src/core/Core.ts b/src/core/Core.ts new file mode 100644 index 0000000..455f024 --- /dev/null +++ b/src/core/Core.ts @@ -0,0 +1,37 @@ +import { Logger } from "./logs/Logger"; +import { Timer } from "./Timer"; + +class Core { + private _logger?: Logger; + private _timers = new Map(); + + get logger(): Logger { + return (this._logger ??= new Logger()); + } + + startTimer(id: string): void { + if (!this._timers.get(id)) { + this._timers.set(id, new Timer()); + } + } + + stopTimer(id: string): Timer { + const timer = this._timers.get(id); + if (!timer) throw new Error(`Timer ${id} not found`); + this._timers.delete(id); + return timer; + } + + queryTimer(id: string): Timer { + const timer = this._timers.get(id); + if (!timer) throw new Error(`Timer ${id} not found`); + return timer; + } + + reset(): void { + this._logger = undefined; + this._timers.clear(); + } +} + +export const core = new Core(); diff --git a/src/core/Dependencies.ts b/src/core/Dependencies.ts new file mode 100644 index 0000000..3d5ad57 --- /dev/null +++ b/src/core/Dependencies.ts @@ -0,0 +1,7 @@ +class Dependencies { + constructor() {} + + resetAll(): void {} +} + +export const dependencies = new Dependencies(); diff --git a/src/core/Timer.ts b/src/core/Timer.ts new file mode 100644 index 0000000..1ad54b6 --- /dev/null +++ b/src/core/Timer.ts @@ -0,0 +1,52 @@ +import type { TimerResult, TimeUnit } from "../types/time/timer"; + +interface UnitConfig { + label: string; + factor: number; // Multiply raw ms by this to get the unit + threshold: number; // For auto-selection (in ms) +} + +export class Timer { + startTime: number; + + constructor() { + this.startTime = performance.now(); + } + + /** + * Get the elapsed time since the timer started. + * + * @param unit (default = auto) The time unit to use for the adjusted time. + * @param precision (default = 2) Number of decimal places to include in the formatted time. + * + * @returns An object containing three representations of the elapsed time: + * - `raw`: The raw elapsed time in milliseconds (always in ms regardless of unit parameter) + * - `adjusted`: The elapsed time converted to the specified unit + * - `formatted`: A human-readable string with the adjusted time and unit label (e.g., "123.45ms") + */ + getTime(unit: TimeUnit | "auto" = "auto", precision: number = 2): TimerResult { + const raw = performance.now() - this.startTime; + const selectedUnit = unit === "auto" ? this.selectUnit(raw) : unit; + const appConfig = Timer.UNITS[selectedUnit]; + + const adjusted = raw * appConfig.factor; + + const formatted = `${adjusted.toFixed(precision)}${appConfig.label}`; + + return { raw, adjusted, formatted }; + } + + private static readonly UNITS: Record = { + micro: { label: "μs", factor: 1000, threshold: 1 }, + ms: { label: "ms", factor: 1, threshold: 1000 }, + s: { label: "s", factor: 1 / 1000, threshold: 60000 }, + m: { label: "m", factor: 1 / 60000, threshold: -1 }, + }; + + private selectUnit(timeMs: number): TimeUnit { + if (timeMs < Timer.UNITS.micro.threshold) return "micro"; + if (timeMs < Timer.UNITS.ms.threshold) return "ms"; + if (timeMs < Timer.UNITS.s.threshold) return "s"; + return "m"; + } +} diff --git a/src/db/DbHandler.ts b/src/db/DbHandler.ts index 324f6a6..a0fee97 100644 --- a/src/db/DbHandler.ts +++ b/src/db/DbHandler.ts @@ -1,181 +1,149 @@ import { getRedisClient } from "../clients/RedisClient"; import { config } from "../config/config"; +import { getLogger } from "../helpers/Logger"; import { findDcUsernameById } from "../utils/bot/usernames"; -import { getLogger } from "../utils/Logger"; let dbHandler: DbHandler | null = null; export class DbHandler { - client; - - constructor() { - this.client = getRedisClient().client; - } - - async init() { - await this.createGlobalRatesForAllEndpoints(); - } - - async createGlobalRatesForAllEndpoints() { - for (const [endpointName, endpointConfig] of Object.entries( - config.ENDPOINTS - )) { - await this.client.set( - `${endpointName}:GlobalRates`, - endpointConfig.RATE_LIMIT.GLOBAL.MAX_STORED - ); - getLogger().simpleLog( - "success", - `Global Rates Created for ${endpointName}` - ); + client; + + constructor() { + this.client = getRedisClient().client; + } + + async init() { + await this.createGlobalRatesForAllEndpoints(); } - } - - async createUser(discordId: string, endpointName: string, maxStored: number) { - const username = await findDcUsernameById(discordId); - if (!username) { - getLogger().simpleLog( - "warn", - `Could not find discord username for ${discordId}` - ); - return false; + + async createGlobalRatesForAllEndpoints() { + for (const [endpointName, endpointConfig] of Object.entries(config.ENDPOINTS)) { + await this.client.set(`${endpointName}:GlobalRates`, endpointConfig.RATE_LIMIT.GLOBAL.MAX_STORED); + getLogger().simpleLog("success", `Global Rates Created for ${endpointName}`); + } } - await this.client.hSet(`${endpointName}:user:${discordId}`, { - username: username, - rates: maxStored, - }); - return true; - } - - async getGlobalRates(endpointName: string) { - const rates = await this.client.get(`${endpointName}:GlobalRates`); - if (!rates) { - getLogger().simpleLog( - "warn", - `Error Fetching Global Rates for ${endpointName}` - ); - return null; + + async createUser(discordId: string, endpointName: string, maxStored: number) { + const username = await findDcUsernameById(discordId); + if (!username) { + getLogger().simpleLog("warn", `Could not find discord username for ${discordId}`); + return false; + } + await this.client.hSet(`${endpointName}:user:${discordId}`, { + username: username, + rates: maxStored, + }); + return true; + } + + async getGlobalRates(endpointName: string) { + const rates = await this.client.get(`${endpointName}:GlobalRates`); + if (!rates) { + getLogger().simpleLog("warn", `Error Fetching Global Rates for ${endpointName}`); + return null; + } + return parseInt(rates); } - return parseInt(rates); - } - async getUsername(discordId: string, endpointName: string) { - const userData = await this.getUserData(discordId, endpointName); - if (!userData.username) { - throw new Error(`No username found for Discord ID: ${discordId}`); + async getUsername(discordId: string, endpointName: string) { + const userData = await this.getUserData(discordId, endpointName); + if (!userData.username) { + throw new Error(`No username found for Discord ID: ${discordId}`); + } + return userData.username; } - return userData.username; - } - - async getUserRates(discordId: string, endpointName: string) { - const userData = await this.getUserData(discordId, endpointName); - if (!userData.rates) { - throw new Error( - `No rates found for Discord ID: ${discordId} on ${endpointName}` - ); + + async getUserRates(discordId: string, endpointName: string) { + const userData = await this.getUserData(discordId, endpointName); + if (!userData.rates) { + throw new Error(`No rates found for Discord ID: ${discordId} on ${endpointName}`); + } + return parseInt(userData.rates); } - return parseInt(userData.rates); - } - - async updateGlobalRates(endpointName: string, type: "give" | "take") { - const rates = await this.getGlobalRates(endpointName); - if (rates === null) return; - const globalConfig = config.ENDPOINTS[endpointName].RATE_LIMIT.GLOBAL; - let newRates: number; - let tmpRates: number; - - switch (type) { - case "give": - tmpRates = rates + globalConfig.AMOUNT; - newRates = - tmpRates >= globalConfig.MAX_STORED - ? globalConfig.MAX_STORED - : tmpRates; - break; - case "take": - tmpRates = rates - 1; - newRates = tmpRates <= 0 ? 0 : tmpRates; - break; + + async updateGlobalRates(endpointName: string, type: "give" | "take") { + const rates = await this.getGlobalRates(endpointName); + if (rates === null) return; + const globalConfig = config.ENDPOINTS[endpointName].RATE_LIMIT.GLOBAL; + let newRates: number; + let tmpRates: number; + + switch (type) { + case "give": + tmpRates = rates + globalConfig.AMOUNT; + newRates = tmpRates >= globalConfig.MAX_STORED ? globalConfig.MAX_STORED : tmpRates; + break; + case "take": + tmpRates = rates - 1; + newRates = tmpRates <= 0 ? 0 : tmpRates; + break; + } + + await this.client.set(`${endpointName}:GlobalRates`, newRates); } - await this.client.set(`${endpointName}:GlobalRates`, newRates); - } - - async updateUserRates( - discordId: string, - endpointName: string, - type: "give" | "take" - ) { - const rates = await this.getUserRates(discordId, endpointName); - const userConfig = config.ENDPOINTS[endpointName].RATE_LIMIT.USER; - let newRates: number; - let tmpRates: number; - - switch (type) { - case "give": - tmpRates = rates + userConfig.AMOUNT; - newRates = - tmpRates >= userConfig.MAX_STORED ? userConfig.MAX_STORED : tmpRates; - break; - case "take": - tmpRates = rates - 1; - newRates = tmpRates <= 0 ? 0 : tmpRates; - break; + async updateUserRates(discordId: string, endpointName: string, type: "give" | "take") { + const rates = await this.getUserRates(discordId, endpointName); + const userConfig = config.ENDPOINTS[endpointName].RATE_LIMIT.USER; + let newRates: number; + let tmpRates: number; + + switch (type) { + case "give": + tmpRates = rates + userConfig.AMOUNT; + newRates = tmpRates >= userConfig.MAX_STORED ? userConfig.MAX_STORED : tmpRates; + break; + case "take": + tmpRates = rates - 1; + newRates = tmpRates <= 0 ? 0 : tmpRates; + break; + } + + await this.client.hSet(`${endpointName}:user:${discordId}`, "rates", newRates); } - await this.client.hSet( - `${endpointName}:user:${discordId}`, - "rates", - newRates - ); - } - - async getUserData(discordId: string, endpointName: string) { - const key = `${endpointName}:user:${discordId}`; - const exists = !!(await this.client.exists(key)); - if (!exists) { - const maxStored = - config.ENDPOINTS[endpointName].RATE_LIMIT.USER.MAX_STORED; - const created = await this.createUser(discordId, endpointName, maxStored); - if (!created) { - throw new Error( - `Failed to create user for Discord ID: ${discordId} on ${endpointName}` - ); - } + async getUserData(discordId: string, endpointName: string) { + const key = `${endpointName}:user:${discordId}`; + const exists = Boolean(await this.client.exists(key)); + if (!exists) { + const maxStored = config.ENDPOINTS[endpointName].RATE_LIMIT.USER.MAX_STORED; + const created = await this.createUser(discordId, endpointName, maxStored); + if (!created) { + throw new Error(`Failed to create user for Discord ID: ${discordId} on ${endpointName}`); + } + } + return this.client.hGetAll(key); } - return await this.client.hGetAll(key); - } - async getAllUserKeysForEndpoint(endpointName: string) { - const pattern = `${endpointName}:user:*`; - return await this.client.keys(pattern); - } + async getAllUserKeysForEndpoint(endpointName: string) { + const pattern = `${endpointName}:user:*`; + return this.client.keys(pattern); + } - async createHog(discordIds: string[]) { - await this.client.sAdd("members", discordIds); - } + async createHog(discordIds: string[]) { + await this.client.sAdd("members", discordIds); + } - async getHogMembers() { - return await this.client.sMembers("members"); - } + async getHogMembers() { + return this.client.sMembers("members"); + } - async addToHog(discordId: string) { - await this.client.sAdd("members", discordId); - } + async addToHog(discordId: string) { + await this.client.sAdd("members", discordId); + } - async removeFromHog(discordId: string) { - await this.client.sRem("members", discordId); - } + async removeFromHog(discordId: string) { + await this.client.sRem("members", discordId); + } } export function initDbHandler(): DbHandler { - if (dbHandler) return dbHandler; - dbHandler = new DbHandler(); - return dbHandler; + if (dbHandler) return dbHandler; + dbHandler = new DbHandler(); + return dbHandler; } export function getDbHandler(): DbHandler { - if (!dbHandler) - throw new Error("Db Handler not initialized. Call initDbHandler() first."); - return dbHandler; + if (!dbHandler) throw new Error("Db Handler not initialized. Call initDbHandler() first."); + return dbHandler; } diff --git a/src/discord.ts b/src/discord.ts deleted file mode 100644 index b2c2696..0000000 --- a/src/discord.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { BotEventHandler } from "./bot/EventHandler"; -import { initDiscordClient, getDiscordClient } from "./clients/DiscordClient"; - -export async function initDiscord() { - initDiscordClient(); - const client = getDiscordClient().client; - const eventHandler = new BotEventHandler(); - - client.once("clientReady", () => eventHandler.onReady()); - client.on("guildMemberAdd", (member) => eventHandler.onNewMember(member)); - client.on("guildMemberRemove", (member) => - eventHandler.onRemoveMember(member) - ); - - await getDiscordClient().connect(); -} diff --git a/src/express.ts b/src/express.ts deleted file mode 100644 index e03d491..0000000 --- a/src/express.ts +++ /dev/null @@ -1,34 +0,0 @@ -import express from "express"; -import { getLogger } from "./utils/Logger"; -import { routes } from "./routes"; - -export function initExpress() { - const app = express(); - - app.use(routes()); - - app.use( - ( - err: Error, - req: express.Request, - res: express.Response, - next: express.NextFunction - ) => { - getLogger().simpleLog("error", `Unhandled error: ${err.message}`); - console.error(err.stack); - - if (!res.headersSent) { - res.status(500).json({ - error: "Internal Server Error", - message: - process.env.NODE_ENV === "development" - ? err.message - : "Something went wrong", - }); - } - } - ); - - getLogger().simpleLog("success", "Express App Initialized Successfully"); - return app; -} diff --git a/src/helpers/Logger.ts b/src/helpers/Logger.ts new file mode 100644 index 0000000..2ba4b3c --- /dev/null +++ b/src/helpers/Logger.ts @@ -0,0 +1,173 @@ +import { config } from "../config/config"; +import type { LogType } from "../types/server"; +import { queryTimer } from "./Timer"; + +let logger: Logger | null = null; + +enum AnsiColor { + ERROR = "\x1b[31m", + SUCCESS = "\x1b[32m", + WARN = "\x1b[33m", + TELEMETRY = "\x1b[34m", + DEBUG = "\x1b[35m", + INFO = "\x1b[36m", + FORMAT = "\x1b[37m", + RESET = "\x1b[0m", +} + +enum FormattingConstant { + MAX_TYPE_LENGTH = 9, // "success".length + PROGRESS_BAR_FILLED = "█", + PROGRESS_BAR_EMPTY = "░", + FORMATTING_DASH = "-", + FORMATTING_TARGET_WIDTH = 50, + SECONDS_PER_MINUTE = 60, + MILLISECONDS_PER_SECOND = 1000, +} + +export class Logger { + private name: string; + + constructor() { + this.name = config.LOGGER_NAME; + } + + simpleLog(type: LogType, message: string): void { + const timestamp = this.getCurrentTimestamp(); + const logPrefix = this.formatLogPrefix(type, timestamp); + const coloredMessage = this.colorize(message, type); + + console.log(`${logPrefix}: ${coloredMessage}`); + } + + formattingLog(title: string): void { + const timestamp = this.getCurrentTimestamp(); + const logPrefix = this.formatLogPrefix("format", timestamp); + const formattedTitle = this.formatTitle(title); + const coloredTitle = this.colorize(formattedTitle, "format"); + + console.log(`${logPrefix}: ${coloredTitle}`); + } + + progressBar( + currentCount: number, + totalCount: number, + timerId: string, + barWidth: number = 50, + label: string = "", + ): void { + const progress = this.buildProgressBar(currentCount, totalCount, timerId, barWidth, label); + + const coloredProgress = this.colorize(progress, "info"); + process.stdout.write(`\r${coloredProgress}`); + + const isComplete = currentCount === totalCount; + if (isComplete) { + process.stdout.write("\n"); + } + } + + /* + * Formatting helpers + */ + + private formatLogPrefix(type: LogType | "format", timestamp: string): string { + const logName = `[${this.name}:${type.toUpperCase()}]`; + const logTimestamp = `[${timestamp}]`; + const padding = " ".repeat(FormattingConstant.MAX_TYPE_LENGTH - type.length); + + return `${logName}${padding}@${logTimestamp}`; + } + + private formatTitle(title: string): string { + const dashCount = (FormattingConstant.FORMATTING_TARGET_WIDTH - title.length) / 2; + const dashes = (FormattingConstant.FORMATTING_DASH as string).repeat(dashCount); + const hasOddLength = dashCount % 1 !== 0; + const extraDash = hasOddLength ? FormattingConstant.FORMATTING_DASH : ""; + + return `|${dashes} ${title} ${dashes}${extraDash}|`; + } + + private buildProgressBar( + currentCount: number, + totalCount: number, + timerId: string, + barWidth: number, + label: string, + ): string { + const percentage = Math.floor((currentCount / totalCount) * 100); + const bar = this.createBar(currentCount, totalCount, barWidth); + const timeInfo = this.createTimeInfo(currentCount, totalCount, timerId); + const labelText = label ? ` ${label}` : ""; + + return `[${bar}] ${percentage}% (${currentCount}/${totalCount})${labelText} ${timeInfo}`; + } + + private createBar(currentCount: number, totalCount: number, barWidth: number): string { + const filledWidth = Math.floor((currentCount / totalCount) * barWidth); + const emptyWidth = Math.max(0, barWidth - filledWidth); + + const filled = (FormattingConstant.PROGRESS_BAR_FILLED as string).repeat(filledWidth); + const empty = (FormattingConstant.PROGRESS_BAR_EMPTY as string).repeat(emptyWidth); + + return `${filled}${empty}`; + } + + private createTimeInfo(currentCount: number, totalCount: number, timerId: string): string { + const elapsedMilliseconds = queryTimer(timerId).getTime("ms").raw; + const elapsedSeconds = Math.floor(elapsedMilliseconds / FormattingConstant.MILLISECONDS_PER_SECOND); + const elapsedFormatted = this.formatTime(elapsedSeconds); + + let etaText = ""; + const isInProgress = currentCount < totalCount && currentCount > 0; + + if (isInProgress) { + const remainingMilliseconds = (elapsedMilliseconds / currentCount) * (totalCount - currentCount); + const remainingSeconds = Math.floor( + remainingMilliseconds / FormattingConstant.MILLISECONDS_PER_SECOND, + ); + const etaFormatted = this.formatTime(remainingSeconds); + etaText = ` ETA: ${etaFormatted}`; + } + + return `[${elapsedFormatted}]${etaText}`; + } + + private formatTime(totalSeconds: number): string { + const minutes = Math.floor(totalSeconds / FormattingConstant.SECONDS_PER_MINUTE); + const seconds = totalSeconds % FormattingConstant.SECONDS_PER_MINUTE; + const paddedSeconds = seconds.toString().padStart(2, "0"); + + return `${minutes}:${paddedSeconds}`; + } + + private colorize(text: string, type: LogType | "format"): string { + const colorMap: Record = { + success: AnsiColor.SUCCESS, + info: AnsiColor.INFO, + warn: AnsiColor.WARN, + error: AnsiColor.ERROR, + debug: AnsiColor.DEBUG, + telemetry: AnsiColor.TELEMETRY, + format: AnsiColor.FORMAT, + }; + + const color = colorMap[type]; + return `${color}${text}${AnsiColor.RESET}`; + } + + private getCurrentTimestamp(): string { + return new Date().toISOString(); + } +} + +export function initLogger(): Logger { + if (logger) return logger; + logger = new Logger(); + return logger; +} + +export function getLogger(): Logger { + if (!logger) throw new Error("Logger not initialized. Call initLogger() first."); + return logger; +} diff --git a/src/helpers/Timer.ts b/src/helpers/Timer.ts new file mode 100644 index 0000000..1ac9dcb --- /dev/null +++ b/src/helpers/Timer.ts @@ -0,0 +1,84 @@ +import type { TimerRes } from "../types/timer"; + +const timers: Map = new Map(); + +export class Timer { + startTime: number; + + constructor() { + this.startTime = performance.now(); + } + + getTime(unit: "micro" | "ms" | "s" | "m" | "auto" = "auto", precision: number = 2): TimerRes { + const timeTaken_ms = performance.now() - this.startTime; + let adjustedTime: number = 0; + let unitLabel: string = ""; + + switch (unit) { + case "auto": + if (timeTaken_ms < 1) { + adjustedTime = timeTaken_ms * 1000; + unitLabel = "μs"; + } else if (timeTaken_ms < 1000) { + adjustedTime = timeTaken_ms; + unitLabel = "ms"; + } else if (timeTaken_ms < 60000) { + adjustedTime = timeTaken_ms / 1000; + unitLabel = "s"; + } else { + adjustedTime = timeTaken_ms / 60000; + unitLabel = "m"; + } + break; + + case "micro": + adjustedTime = timeTaken_ms * 1000; + unitLabel = "μs"; + break; + + case "ms": + adjustedTime = timeTaken_ms; + unitLabel = "ms"; + break; + + case "s": + adjustedTime = timeTaken_ms / 1000; + unitLabel = "s"; + break; + + case "m": + adjustedTime = timeTaken_ms / 60000; + unitLabel = "m"; + break; + } + + const formattedTime = `${adjustedTime.toFixed(precision)}${unitLabel}`; + + return { + raw: timeTaken_ms, + adjusted: adjustedTime, + formatted: formattedTime, + }; + } +} + +export function startTimer(id: string): void { + if (!timers.get(id)) timers.set(id, new Timer()); +} + +export function stopTimer(id: string): Timer { + const time = timers.get(id); + if (!time) throw new Error(`Timer ${id} not initialized. Call startTimer(id:string) first.`); + timers.delete(id); + return time; +} + +export function queryTimer(id: string): Timer { + const time = timers.get(id); + if (!time) throw new Error(`Timer ${id} not initialized. Call startTimer(id:string) first.`); + return time; +} + +export function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/src/loaders/files.ts b/src/loaders/files.ts index 0ed7dba..df9630c 100644 --- a/src/loaders/files.ts +++ b/src/loaders/files.ts @@ -1,5 +1,5 @@ -import path from "path"; import fs from "fs"; +import path from "path"; const STORAGE_DIR = path.join(process.cwd(), "storage"); const CONFIG_DIR = path.join(STORAGE_DIR, "config"); @@ -15,15 +15,12 @@ export const NOMICORD_PROMPT_FILE = path.join(PROMPTS_DIR, "nomicord.txt"); export const FILTERS_FILES_DIR = path.join(STORAGE_DIR, "filters"); export const FILTERS_FILES = new Map(); fs.readdirSync(FILTERS_FILES_DIR).forEach((file) => - FILTERS_FILES.set( - file.replace(".json", ""), - path.join(FILTERS_FILES_DIR, file) - ) + FILTERS_FILES.set(file.replace(".json", ""), path.join(FILTERS_FILES_DIR, file)), ); export const OREDIC_DIR = path.join(STORAGE_DIR, "oredic"); export const DUMPS_DIR = path.join(OREDIC_DIR, "dumps"); export const DUMPS_FILES = new Map(); fs.readdirSync(DUMPS_DIR).forEach((file) => - DUMPS_FILES.set(file.replace(".txt", ""), path.join(DUMPS_DIR, file)) + DUMPS_FILES.set(file.replace(".txt", ""), path.join(DUMPS_DIR, file)), ); diff --git a/src/loaders/filters.ts b/src/loaders/filters.ts index 7c7176e..f895933 100644 --- a/src/loaders/filters.ts +++ b/src/loaders/filters.ts @@ -1,22 +1,23 @@ import fs from "fs"; -import { Filter, PatternConfig } from "../types/server"; -import { FILTERS_FILES } from "./files"; + import { filtersConfig } from "../config/config"; +import type { Filter, PatternConfig } from "../types/server"; +import { FILTERS_FILES } from "./files"; -export let FILTERS: Filter[] = []; +export const FILTERS: Filter[] = []; FILTERS_FILES.forEach((value, key) => { - if (!filtersConfig.grok.enabledFilters.includes(key)) { - return; - } + if (!filtersConfig.grok.enabledFilters.includes(key)) { + return; + } - const object = JSON.parse(fs.readFileSync(value, "utf-8")); + const object = JSON.parse(fs.readFileSync(value, "utf-8")); - object.patterns.forEach((element: PatternConfig) => { - const filter: Filter = { - pattern: new RegExp(`${element.pattern}`, "gi"), - severity: element.severity, - description: element.description, - }; - FILTERS.push(filter); - }); + object.patterns.forEach((element: PatternConfig) => { + const filter: Filter = { + pattern: new RegExp(`${element.pattern}`, "gi"), + severity: element.severity, + description: element.description, + }; + FILTERS.push(filter); + }); }); diff --git a/src/loaders/keys.ts b/src/loaders/keys.ts index 2a3c108..fa074bd 100644 --- a/src/loaders/keys.ts +++ b/src/loaders/keys.ts @@ -1,8 +1,9 @@ import { createPublicKey } from "crypto"; + import { env } from "../config/config"; export const LEVERET_PUBLIC_KEY = createPublicKey({ - key: Buffer.from(env.LEVERET_PUB_KEY_B64, "base64"), - format: "der", - type: "spki", + key: Buffer.from(env.LEVERET_PUB_KEY_B64, "base64"), + format: "der", + type: "spki", }); diff --git a/src/loaders/storage.ts b/src/loaders/storage.ts index bb6b72d..e4124a3 100644 --- a/src/loaders/storage.ts +++ b/src/loaders/storage.ts @@ -1,25 +1,22 @@ import fs from "fs"; + import { - CONFIG_FILE, - DEFAULT_PROMPT_FILE, - DUMPS_FILES, - FILTERS_CONFIG_FILE, - HOGICHAN_PROMPT_FILE, - NOMICORD_PROMPT_FILE, + CONFIG_FILE, + DEFAULT_PROMPT_FILE, + DUMPS_FILES, + FILTERS_CONFIG_FILE, + HOGICHAN_PROMPT_FILE, + NOMICORD_PROMPT_FILE, } from "./files"; export const RAW_CONFIG = JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8")); -export const RAW_FILTERS_CONFIG = JSON.parse( - fs.readFileSync(FILTERS_CONFIG_FILE, "utf-8") -); +export const RAW_FILTERS_CONFIG = JSON.parse(fs.readFileSync(FILTERS_CONFIG_FILE, "utf-8")); export const DEFAULT_PROMPT = fs.readFileSync(DEFAULT_PROMPT_FILE, "utf-8"); -export const HOGICHAN_PROMPT = - DEFAULT_PROMPT + "\n" + fs.readFileSync(HOGICHAN_PROMPT_FILE, "utf-8"); -export const NOMICORD_PROMPT = - DEFAULT_PROMPT + "\n" + fs.readFileSync(NOMICORD_PROMPT_FILE, "utf-8"); +export const HOGICHAN_PROMPT = DEFAULT_PROMPT + "\n" + fs.readFileSync(HOGICHAN_PROMPT_FILE, "utf-8"); +export const NOMICORD_PROMPT = DEFAULT_PROMPT + "\n" + fs.readFileSync(NOMICORD_PROMPT_FILE, "utf-8"); export const DUMPS = new Map(); DUMPS_FILES.forEach((file, fileName) => { - DUMPS.set(fileName, fs.readFileSync(file, "utf-8")); + DUMPS.set(fileName, fs.readFileSync(file, "utf-8")); }); diff --git a/src/middleware/analytics/perf/timer.ts b/src/middleware/analytics/perf/timer.ts index 23d17fa..3e02d24 100644 --- a/src/middleware/analytics/perf/timer.ts +++ b/src/middleware/analytics/perf/timer.ts @@ -1,23 +1,21 @@ -import { Request, Response, NextFunction } from "express"; -import { startTimer, stopTimer } from "../../../utils/Timer"; -import { getLogger } from "../../../utils/Logger"; +import type { NextFunction, Request, Response } from "express"; + +import { getLogger } from "../../../helpers/Logger"; +import { startTimer, stopTimer } from "../../../helpers/Timer"; export const timerStart = (req: Request, res: Response, next: NextFunction) => { - req.timerId = `req-${Date.now()}`; - startTimer(req.timerId); + req.timerId = `req-${Date.now()}`; + startTimer(req.timerId); - next(); + next(); }; export const timerStop = (req: Request, res: Response, next: NextFunction) => { - const timing = stopTimer(req.timerId).getTime("ms", 0); + const timing = stopTimer(req.timerId).getTime("ms", 0); - const endpoint = req.endpoint || "unknown"; - getLogger().simpleLog( - "telemetry", - `Served ${endpoint} in ${timing.formatted}` - ); + const endpoint = req.endpoint || "unknown"; + getLogger().simpleLog("telemetry", `Served ${endpoint} in ${timing.formatted}`); - res.timing = timing; - next(); + res.timing = timing; + next(); }; diff --git a/src/middleware/auth/globalRateLimits.ts b/src/middleware/auth/globalRateLimits.ts index c950e09..4cf9730 100644 --- a/src/middleware/auth/globalRateLimits.ts +++ b/src/middleware/auth/globalRateLimits.ts @@ -1,49 +1,36 @@ -import { Request, Response, NextFunction } from "express"; -import { getDbHandler } from "../../db/DbHandler"; -import { getLogger } from "../../utils/Logger"; +import type { NextFunction, Request, Response } from "express"; + import { config } from "../../config/config"; +import { getDbHandler } from "../../db/DbHandler"; +import { getLogger } from "../../helpers/Logger"; -export const globalRateLimits = async ( - req: Request, - res: Response, - next: NextFunction -): Promise => { - const aiModel = req.ai?.model; +export const globalRateLimits = async (req: Request, res: Response, next: NextFunction): Promise => { + const aiModel = req.ai?.model; - if (!aiModel) { - res.status(500).json({ error: "AI model not found" }); - return; - } + if (!aiModel) { + res.status(500).json({ error: "AI model not found" }); + return; + } - const modelConfig = config.ENDPOINTS[aiModel]; - if (!modelConfig) { - res.status(500).json({ error: "Model configuration not found" }); - return; - } + const modelConfig = config.ENDPOINTS[aiModel]; + if (!modelConfig) { + res.status(500).json({ error: "Model configuration not found" }); + return; + } - const isAllowed = await checkRateLimits( - req.body.userId, - aiModel, - modelConfig.RATE_LIMIT.USER.WHITELIST - ); - if (!isAllowed) { - getLogger().simpleLog("warn", `Global Rate Limit Reached for ${aiModel}`); - res - .status(429) - .json({ error: "You're Being Rate Limited by the Global Rate Limit" }); - return; - } - await getDbHandler().updateGlobalRates(aiModel, "take"); - next(); + const isAllowed = await checkRateLimits(req.body.userId, aiModel, modelConfig.RATE_LIMIT.USER.WHITELIST); + if (!isAllowed) { + getLogger().simpleLog("warn", `Global Rate Limit Reached for ${aiModel}`); + res.status(429).json({ error: "You're Being Rate Limited by the Global Rate Limit" }); + return; + } + await getDbHandler().updateGlobalRates(aiModel, "take"); + next(); }; -async function checkRateLimits( - dcUserId: string, - modelName: string, - whitelist: string[] -) { - if (whitelist.includes(dcUserId)) return true; - const rates = await getDbHandler().getGlobalRates(modelName); - if (rates === null) return false; - return rates > 0; +async function checkRateLimits(dcUserId: string, modelName: string, whitelist: string[]) { + if (whitelist.includes(dcUserId)) return true; + const rates = await getDbHandler().getGlobalRates(modelName); + if (rates === null) return false; + return rates > 0; } diff --git a/src/middleware/auth/leveretAuth.ts b/src/middleware/auth/leveretAuth.ts index cee8c58..f03c88d 100644 --- a/src/middleware/auth/leveretAuth.ts +++ b/src/middleware/auth/leveretAuth.ts @@ -1,83 +1,67 @@ -import { Request, Response, NextFunction } from "express"; -import { config } from "../../config/config"; import { createHash, verify } from "crypto"; +import type { NextFunction, Request, Response } from "express"; + +import { config } from "../../config/config"; +import { getLogger } from "../../helpers/Logger"; import { LEVERET_PUBLIC_KEY } from "../../loaders/keys"; -import { getLogger } from "../../utils/Logger"; -export const leveretAuth = ( - req: Request, - res: Response, - next: NextFunction -): void => { - const authResult = authRequest(req); +export const leveretAuth = (req: Request, res: Response, next: NextFunction): void => { + const authResult = authRequest(req); - if (!authResult) { - getLogger().simpleLog("warn", "Unauthorized Request For Leveret"); - res.status(401).json({ error: "Unauthorized" }); - return; - } - getLogger().simpleLog("telemetry", "Authorized Leveret Request"); - req.flags = { isLeveret: true }; - next(); + if (!authResult) { + getLogger().simpleLog("warn", "Unauthorized Request For Leveret"); + res.status(401).json({ error: "Unauthorized" }); + return; + } + getLogger().simpleLog("telemetry", "Authorized Leveret Request"); + req.flags = { isLeveret: true }; + next(); }; const HEADERS = { - signature: "x-leveret-signature-ed25519", - tag: "x-leveret-tag", - timestamp: "x-leveret-timestamp", - requestId: "x-leveret-request-id", + signature: "x-leveret-signature-ed25519", + tag: "x-leveret-tag", + timestamp: "x-leveret-timestamp", + requestId: "x-leveret-request-id", }; function authRequest(req: any) { - if (config.SKIP_LEVERET_AUTH) return true; + if (config.SKIP_LEVERET_AUTH) return true; - // Mostly from https://gist.github.com/NotMyWing/632d738644c17aa71931169af5cb2767 - const headers = req.headers ?? {}; - const signatureB64 = headers[HEADERS.signature]; - const tagName = headers[HEADERS.tag]; - const timestamp = headers[HEADERS.timestamp]; - const requestId = headers[HEADERS.requestId]; + // Mostly from https://gist.github.com/NotMyWing/632d738644c17aa71931169af5cb2767 + const headers = req.headers ?? {}; + const signatureB64 = headers[HEADERS.signature]; + const tagName = headers[HEADERS.tag]; + const timestamp = headers[HEADERS.timestamp]; + const requestId = headers[HEADERS.requestId]; - // Do less expensive checks first to save on potential compute - if (!signatureB64 || !tagName) { - getLogger().simpleLog( - "warn", - "LEVE_AUTH: Could not find signatureB64 nor tagName" - ); - return false; - } - if (!config.ACCEPTED_TAGS.includes(tagName)) { - getLogger().simpleLog( - "warn", - "LEVE_AUTH: Could not find tagName in the accepted tags" - ); - return false; - } + // Do less expensive checks first to save on potential compute + if (!signatureB64 || !tagName) { + getLogger().simpleLog("warn", "LEVE_AUTH: Could not find signatureB64 nor tagName"); + return false; + } + if (!config.ACCEPTED_TAGS.includes(tagName)) { + getLogger().simpleLog("warn", "LEVE_AUTH: Could not find tagName in the accepted tags"); + return false; + } - const method = String(req.method ?? "GET").toUpperCase(); - const host = req.get?.("host") ?? headers.host; - if (!host) { - getLogger().simpleLog("warn", "LEVE_AUTH: Could not find host"); - return false; - } + const method = String(req.method ?? "GET").toUpperCase(); + const host = req.get?.("host") ?? headers.host; + if (!host) { + getLogger().simpleLog("warn", "LEVE_AUTH: Could not find host"); + return false; + } - const url = `${req.protocol ?? "https"}://${host}${ - req.originalUrl ?? req.url ?? "" - }`; - const bodyHash = createHash("sha256").update(req.rawBody).digest("hex"); - const canonical = [timestamp, requestId, method, url, bodyHash].join("\n"); + const url = `${req.protocol ?? "https"}://${host}${req.originalUrl ?? req.url ?? ""}`; + const bodyHash = createHash("sha256").update(req.rawBody).digest("hex"); + const canonical = [timestamp, requestId, method, url, bodyHash].join("\n"); - const signature = Buffer.from(signatureB64, "base64"); - const valid = verify( - null, - Buffer.from(canonical), - LEVERET_PUBLIC_KEY, - signature - ); - if (!valid) { - getLogger().simpleLog("warn", "LEVE_AUTH: Invalid signature"); - return false; - } + const signature = Buffer.from(signatureB64, "base64"); + const valid = verify(null, Buffer.from(canonical), LEVERET_PUBLIC_KEY, signature); + if (!valid) { + getLogger().simpleLog("warn", "LEVE_AUTH: Invalid signature"); + return false; + } - return true; + return true; } diff --git a/src/middleware/auth/userRateLimits.ts b/src/middleware/auth/userRateLimits.ts index 578fc56..887b0f7 100644 --- a/src/middleware/auth/userRateLimits.ts +++ b/src/middleware/auth/userRateLimits.ts @@ -1,52 +1,38 @@ -import { Request, Response, NextFunction } from "express"; -import { getDbHandler } from "../../db/DbHandler"; -import { getLogger } from "../../utils/Logger"; +import type { NextFunction, Request, Response } from "express"; + import { config } from "../../config/config"; +import { getDbHandler } from "../../db/DbHandler"; +import { getLogger } from "../../helpers/Logger"; + +export const userRateLimits = async (req: Request, res: Response, next: NextFunction): Promise => { + const userId = req.body.userId; + const aiModel = req.ai?.model; + + if (!aiModel) { + res.status(500).json({ error: "AI model not found" }); + return; + } + + const modelConfig = config.ENDPOINTS[aiModel]; + if (!modelConfig) { + res.status(500).json({ error: "Model configuration not found" }); + return; + } + + const isAllowed = await checkRateLimits(userId, aiModel, modelConfig.RATE_LIMIT.USER.WHITELIST); + + if (!isAllowed) { + const username = await getDbHandler().getUsername(userId, aiModel); + getLogger().simpleLog("warn", `User: ${username} got rate limited on ${aiModel}`); + res.status(429).json({ error: "You're Being Rate Limited" }); + return; + } -export const userRateLimits = async ( - req: Request, - res: Response, - next: NextFunction -): Promise => { - const userId = req.body.userId; - const aiModel = req.ai?.model; - - if (!aiModel) { - res.status(500).json({ error: "AI model not found" }); - return; - } - - const modelConfig = config.ENDPOINTS[aiModel]; - if (!modelConfig) { - res.status(500).json({ error: "Model configuration not found" }); - return; - } - - const isAllowed = await checkRateLimits( - userId, - aiModel, - modelConfig.RATE_LIMIT.USER.WHITELIST - ); - - if (!isAllowed) { - const username = await getDbHandler().getUsername(userId, aiModel); - getLogger().simpleLog( - "warn", - `User: ${username} got rate limited on ${aiModel}` - ); - res.status(429).json({ error: "You're Being Rate Limited" }); - return; - } - - next(); + next(); }; -async function checkRateLimits( - dcUserId: string, - modelName: string, - whitelist: string[] -) { - if (whitelist.includes(dcUserId)) return true; - const rates = await getDbHandler().getUserRates(dcUserId, modelName); - return rates > 0; +async function checkRateLimits(dcUserId: string, modelName: string, whitelist: string[]) { + if (whitelist.includes(dcUserId)) return true; + const rates = await getDbHandler().getUserRates(dcUserId, modelName); + return rates > 0; } diff --git a/src/middleware/endpoints/aiEndpoint.ts b/src/middleware/endpoints/aiEndpoint.ts index a24c362..f2f391e 100644 --- a/src/middleware/endpoints/aiEndpoint.ts +++ b/src/middleware/endpoints/aiEndpoint.ts @@ -1,44 +1,42 @@ -import { Request, Response, NextFunction } from "express"; -import { config } from "../../config/config"; -import { - AI_MODELS, - SYSTEM_PROMPTS, - AiModel, - SystemPrompt, -} from "../../config/routes"; import "../../types/express"; +import type { NextFunction, Request, Response } from "express"; + +import { config } from "../../config/config"; +import type { AiModel, SystemPrompt } from "../../config/routes"; +import { AI_MODELS, SYSTEM_PROMPTS } from "../../config/routes"; + export const aiEndpoint = (req: Request, res: Response, next: NextFunction) => { - const { model, systemPrompt } = req.params; - - const isValidModel = AI_MODELS.includes(model as AiModel); - if (!isValidModel) { - res.status(404).json({ - error: "Model not found", - message: `Model must be one of: ${AI_MODELS.join(", ")}`, - }); - return; - } - - const isValidPrompt = SYSTEM_PROMPTS.includes(systemPrompt as SystemPrompt); - if (!isValidPrompt) { - res.status(400).json({ - error: "Invalid system prompt", - message: `System prompt must be one of: ${SYSTEM_PROMPTS.join(", ")}`, - }); - return; - } - - const modelConfig = config.ENDPOINTS[model]; - if (!modelConfig) { - res.status(500).json({ error: "Model configuration missing" }); - return; - } - - req.ai = { - model: model as AiModel, - systemPrompt: systemPrompt as SystemPrompt, - }; - - next(); + const { model, systemPrompt } = req.params; + + const isValidModel = AI_MODELS.includes(model as AiModel); + if (!isValidModel) { + res.status(404).json({ + error: "Model not found", + message: `Model must be one of: ${AI_MODELS.join(", ")}`, + }); + return; + } + + const isValidPrompt = SYSTEM_PROMPTS.includes(systemPrompt as SystemPrompt); + if (!isValidPrompt) { + res.status(400).json({ + error: "Invalid system prompt", + message: `System prompt must be one of: ${SYSTEM_PROMPTS.join(", ")}`, + }); + return; + } + + const modelConfig = config.ENDPOINTS[model]; + if (!modelConfig) { + res.status(500).json({ error: "Model configuration missing" }); + return; + } + + req.ai = { + model: model as AiModel, + systemPrompt: systemPrompt as SystemPrompt, + }; + + next(); }; diff --git a/src/middleware/endpoints/loadEndpoint.ts b/src/middleware/endpoints/loadEndpoint.ts index 12f625e..28da2de 100644 --- a/src/middleware/endpoints/loadEndpoint.ts +++ b/src/middleware/endpoints/loadEndpoint.ts @@ -1,12 +1,8 @@ -import { Request, Response, NextFunction } from "express"; +import type { NextFunction, Request, Response } from "express"; -export const loadEndpoint = ( - req: Request, - res: Response, - next: NextFunction -) => { - const endpoint = `${req.baseUrl}${req.path}`; - req.endpoint = endpoint; +export const loadEndpoint = (req: Request, res: Response, next: NextFunction) => { + const endpoint = `${req.baseUrl}${req.path}`; + req.endpoint = endpoint; - next(); + next(); }; diff --git a/src/middleware/endpoints/membersEndpoint.ts b/src/middleware/endpoints/membersEndpoint.ts index 4322d41..720d51b 100644 --- a/src/middleware/endpoints/membersEndpoint.ts +++ b/src/middleware/endpoints/membersEndpoint.ts @@ -1,10 +1,7 @@ -import { Request, Response, NextFunction } from "express"; import "../../types/express"; -export const membersEndpoint = ( - req: Request, - res: Response, - next: NextFunction -) => { - next(); +import type { NextFunction, Request, Response } from "express"; + +export const membersEndpoint = (req: Request, res: Response, next: NextFunction) => { + next(); }; diff --git a/src/middleware/endpoints/oredicEndpoint.ts b/src/middleware/endpoints/oredicEndpoint.ts index e5001f4..7bd3886 100644 --- a/src/middleware/endpoints/oredicEndpoint.ts +++ b/src/middleware/endpoints/oredicEndpoint.ts @@ -1,41 +1,35 @@ -import { Request, Response, NextFunction } from "express"; -import { - OREDIC_PACKS, - OREDIC_ACTIONS, - OredicPack, - OredicAction, -} from "../../config/routes"; import "../../types/express"; -export const oredicEndpoint = ( - req: Request, - res: Response, - next: NextFunction -) => { - const { pack, action } = req.params; +import type { NextFunction, Request, Response } from "express"; - const isValidPack = OREDIC_PACKS.includes(pack as OredicPack); - if (!isValidPack) { - res.status(404).json({ - error: "Invalid pack", - message: `Pack must be one of: ${OREDIC_PACKS.join(", ")}`, - }); - return; - } +import type { OredicAction, OredicPack } from "../../config/routes"; +import { OREDIC_ACTIONS, OREDIC_PACKS } from "../../config/routes"; - const isValidAction = OREDIC_ACTIONS.includes(action as OredicAction); - if (!isValidAction) { - res.status(404).json({ - error: "Invalid action", - message: `Action must be one of: ${OREDIC_ACTIONS.join(", ")}`, - }); - return; - } +export const oredicEndpoint = (req: Request, res: Response, next: NextFunction) => { + const { pack, action } = req.params; - req.oredic = { - pack: pack as OredicPack, - action: action as OredicAction, - }; + const isValidPack = OREDIC_PACKS.includes(pack as OredicPack); + if (!isValidPack) { + res.status(404).json({ + error: "Invalid pack", + message: `Pack must be one of: ${OREDIC_PACKS.join(", ")}`, + }); + return; + } - next(); + const isValidAction = OREDIC_ACTIONS.includes(action as OredicAction); + if (!isValidAction) { + res.status(404).json({ + error: "Invalid action", + message: `Action must be one of: ${OREDIC_ACTIONS.join(", ")}`, + }); + return; + } + + req.oredic = { + pack: pack as OredicPack, + action: action as OredicAction, + }; + + next(); }; diff --git a/src/middleware/sanity/aiTokenChecker.ts b/src/middleware/sanity/aiTokenChecker.ts index 11dcdba..df8297e 100644 --- a/src/middleware/sanity/aiTokenChecker.ts +++ b/src/middleware/sanity/aiTokenChecker.ts @@ -1,64 +1,51 @@ -import { Request, Response, NextFunction } from "express"; -import { getLogger } from "../../utils/Logger"; -import { tokenizeGrok } from "../../utils/grok/grok"; -import { findDcUsernameById } from "../../utils/bot/usernames"; -import { config } from "../../config/config"; - -export const aiTokenChecker = async ( - req: Request, - res: Response, - next: NextFunction -): Promise => { - const aiModel = req.ai?.model; - - if (!aiModel) { - res.status(500).json({ error: "AI model not found" }); - return; - } - - const modelConfig = config.ENDPOINTS[aiModel]; - if (!modelConfig) { - res.status(500).json({ error: "Model configuration not found" }); - return; - } +import type { NextFunction, Request, Response } from "express"; - // TODO: Implement model-specific tokenization based on aiModel - // For now, using Grok tokenizer as default - const tokenizedPrompt = await tokenizeGrok( - req.body.prompt, - modelConfig.MODEL - ); - const tokenizedContext = await tokenizeGrok( - req.body.context, - modelConfig.MODEL - ); - - if (!tokenizedPrompt || !tokenizedContext) { - res.status(502).json({ error: "AI Tokenizer errored" }); - return; - } - - if (tokenizedPrompt.tokenCount > modelConfig.MAX_PROMPT_TK) { - res - .status(403) - .json({ error: "Token count exceeds the maximum allowed limit" }); - return; - } - - if (tokenizedContext.tokenCount > modelConfig.MAX_CONTEXT_TK) { - res - .status(403) - .json({ error: "Context count exceeds the maximum allowed limit" }); - return; - } +import { config } from "../../config/config"; +import { getLogger } from "../../helpers/Logger"; +import { findDcUsernameById } from "../../utils/bot/usernames"; +import { tokenizeGrok } from "../../utils/grok/grok"; - const totalTokens = tokenizedPrompt.tokenCount + tokenizedContext.tokenCount; - const username = await findDcUsernameById(req.body.userId); - getLogger().simpleLog( - "telemetry", - `${username} passed a request for ${totalTokens} tokens on ${aiModel}` - ); - next(); +export const aiTokenChecker = async (req: Request, res: Response, next: NextFunction): Promise => { + const aiModel = req.ai?.model; + + if (!aiModel) { + res.status(500).json({ error: "AI model not found" }); + return; + } + + const modelConfig = config.ENDPOINTS[aiModel]; + if (!modelConfig) { + res.status(500).json({ error: "Model configuration not found" }); + return; + } + + // TODO: Implement model-specific tokenization based on aiModel + // For now, using Grok tokenizer as default + const tokenizedPrompt = await tokenizeGrok(req.body.prompt, modelConfig.MODEL); + const tokenizedContext = await tokenizeGrok(req.body.context, modelConfig.MODEL); + + if (!tokenizedPrompt || !tokenizedContext) { + res.status(502).json({ error: "AI Tokenizer errored" }); + return; + } + + if (tokenizedPrompt.tokenCount > modelConfig.MAX_PROMPT_TK) { + res.status(403).json({ error: "Token count exceeds the maximum allowed limit" }); + return; + } + + if (tokenizedContext.tokenCount > modelConfig.MAX_CONTEXT_TK) { + res.status(403).json({ error: "Context count exceeds the maximum allowed limit" }); + return; + } + + const totalTokens = tokenizedPrompt.tokenCount + tokenizedContext.tokenCount; + const username = await findDcUsernameById(req.body.userId); + getLogger().simpleLog( + "telemetry", + `${username} passed a request for ${totalTokens} tokens on ${aiModel}`, + ); + next(); }; // Legacy export for backwards compatibility diff --git a/src/middleware/sanity/cleanup.ts b/src/middleware/sanity/cleanup.ts index eeee7fd..bfac2f8 100644 --- a/src/middleware/sanity/cleanup.ts +++ b/src/middleware/sanity/cleanup.ts @@ -1,26 +1,22 @@ -import { NextFunction, Response, Request } from "express"; +import type { NextFunction, Request, Response } from "express"; + import { getDbHandler } from "../../db/DbHandler"; -import { getLogger } from "../../utils/Logger"; -export const cleanup = async ( - req: Request, - res: Response, - next: NextFunction -): Promise => { - const isLeveret = req.flags?.isLeveret; +export const cleanup = async (req: Request, res: Response, next: NextFunction): Promise => { + const isLeveret = req.flags?.isLeveret; - if (isLeveret) { - const userId = req.body.userId; - const aiModel = req.ai?.model; + if (isLeveret) { + const userId = req.body.userId; + const aiModel = req.ai?.model; - if (aiModel) { - await getDbHandler().updateGlobalRates(aiModel, "take"); - await getDbHandler().updateUserRates(userId, aiModel, "take"); + if (aiModel) { + await getDbHandler().updateGlobalRates(aiModel, "take"); + await getDbHandler().updateUserRates(userId, aiModel, "take"); + } } - } - res.json({ - ...res.data, - timing: res.timing, - }); + res.json({ + ...res.data, + timing: res.timing, + }); }; diff --git a/src/middleware/sanity/filterBody.ts b/src/middleware/sanity/filterBody.ts index 5632f7f..b655c37 100644 --- a/src/middleware/sanity/filterBody.ts +++ b/src/middleware/sanity/filterBody.ts @@ -1,87 +1,78 @@ -import { Request, Response, NextFunction } from "express"; +import type { NextFunction, Request, Response } from "express"; + import { filtersConfig } from "../../config/config"; -import { FILTERS } from "../../loaders/filters"; -import { getLogger } from "../../utils/Logger"; import { getDbHandler } from "../../db/DbHandler"; +import { getLogger } from "../../helpers/Logger"; +import { FILTERS } from "../../loaders/filters"; //TODO: Make the config a map for Severity -> Action, and handle that. -export const filterBody = async ( - req: Request, - res: Response, - next: NextFunction -): Promise => { - const prompt = req.body.prompt; - const context = req.body.context; - const aiModel = req.ai?.model; +export const filterBody = async (req: Request, res: Response, next: NextFunction): Promise => { + const prompt = req.body.prompt; + const context = req.body.context; + const aiModel = req.ai?.model; - if (!aiModel) { - res.status(500).json("AI model not found"); - return; - } + if (!aiModel) { + res.status(500).json("AI model not found"); + return; + } - const username = await getDbHandler().getUsername(req.body.userId, aiModel); + const username = await getDbHandler().getUsername(req.body.userId, aiModel); - switch (filtersConfig.grok.filterAction) { - case "warn": - filterAndWarn(prompt, username); - filterAndWarn(context, username); - next(); - break; - case "replace": - req.body.prompt = filterAndReplace(prompt, username); - req.body.context = filterAndReplace(context, username); - next(); - break; - case "block": - const promptBlocked = filterAndBlock(prompt, username); - const contextBlocked = filterAndBlock(context, username); + switch (filtersConfig.grok.filterAction) { + case "warn": + filterAndWarn(prompt, username); + filterAndWarn(context, username); + next(); + break; + case "replace": + req.body.prompt = filterAndReplace(prompt, username); + req.body.context = filterAndReplace(context, username); + next(); + break; + case "block": + const promptBlocked = filterAndBlock(prompt, username); + const contextBlocked = filterAndBlock(context, username); - if (!promptBlocked || !contextBlocked) { - res.status(403).json({ - error: "Content Violation", - message: filtersConfig.grok.customMessage, - }); - return; - } - next(); - break; - } + if (!promptBlocked || !contextBlocked) { + res.status(403).json({ + error: "Content Violation", + message: filtersConfig.grok.customMessage, + }); + return; + } + next(); + break; + } }; function filterAndWarn(text: string, username: string) { - FILTERS.forEach((filter) => { - if (filter.pattern.test(text)) { - getLogger().simpleLog( - "warn", - `${filter.description} (${filter.severity}) - User: ${username}` - ); - } - }); + FILTERS.forEach((filter) => { + if (filter.pattern.test(text)) { + getLogger().simpleLog("warn", `${filter.description} (${filter.severity}) - User: ${username}`); + } + }); } function filterAndReplace(text: string, username: string): string { - let result = text; - FILTERS.forEach((filter) => { - if (filter.pattern.test(result)) { - getLogger().simpleLog( - "warn", - `${filter.description} (${filter.severity}) - User: ${username}` - ); - result = result.replace(filter.pattern, "[removed]"); - } - }); - return result; + let result = text; + FILTERS.forEach((filter) => { + if (filter.pattern.test(result)) { + getLogger().simpleLog("warn", `${filter.description} (${filter.severity}) - User: ${username}`); + result = result.replace(filter.pattern, "[removed]"); + } + }); + return result; } function filterAndBlock(text: string, username: string): boolean { - for (const filter of FILTERS) { - if (filter.pattern.test(text)) { - getLogger().simpleLog( - "warn", - `BLOCKED: ${filter.description} (${filter.severity}) - User: ${username}` - ); - return false; + for (const filter of FILTERS) { + if (filter.pattern.test(text)) { + getLogger().simpleLog( + "warn", + `BLOCKED: ${filter.description} (${filter.severity}) - User: ${username}`, + ); + return false; + } } - } - return true; + return true; } diff --git a/src/middleware/sanity/jsonWithRawBody.ts b/src/middleware/sanity/jsonWithRawBody.ts index f402a89..afc2213 100644 --- a/src/middleware/sanity/jsonWithRawBody.ts +++ b/src/middleware/sanity/jsonWithRawBody.ts @@ -1,9 +1,9 @@ import express from "express"; export function jsonWithRawBody() { - return express.json({ - verify: (req: any, _res, buf) => { - req.rawBody = Buffer.from(buf); - }, - }); + return express.json({ + verify: (req: any, _res, buf) => { + req.rawBody = Buffer.from(buf); + }, + }); } diff --git a/src/middleware/sanity/validator.ts b/src/middleware/sanity/validator.ts index 415b8e4..6d2b349 100644 --- a/src/middleware/sanity/validator.ts +++ b/src/middleware/sanity/validator.ts @@ -1,28 +1,26 @@ -import { Request, Response, NextFunction } from "express"; -import { ZodError, ZodObject } from "zod"; -import { getLogger } from "../../utils/Logger"; +import type { NextFunction, Request, Response } from "express"; +import type { ZodObject } from "zod"; + +import { getLogger } from "../../helpers/Logger"; export const zodValidator = (schema: ZodObject) => { - return (req: Request, res: Response, next: NextFunction) => { - const parsed = schema.safeParse(req.body); - if (!parsed.success) { - const errorDetails = parsed.error.issues.map((e: any) => ({ - field: e.path.join("."), - message: e.message, - })); + return (req: Request, res: Response, next: NextFunction) => { + const parsed = schema.safeParse(req.body); + if (!parsed.success) { + const errorDetails = parsed.error.issues.map((e: any) => ({ + field: e.path.join("."), + message: e.message, + })); - getLogger().simpleLog( - "warn", - `Zod validation failed: ${JSON.stringify(errorDetails)}` - ); + getLogger().simpleLog("warn", `Zod validation failed: ${JSON.stringify(errorDetails)}`); - res.status(400).json({ - error: "Validation Error", - details: errorDetails, - }); - return; - } - req.body = parsed.data; - next(); - }; + res.status(400).json({ + error: "Validation Error", + details: errorDetails, + }); + return; + } + req.body = parsed.data; + next(); + }; }; diff --git a/src/routes/ai.ts b/src/routes/ai.ts index dad7029..56d7c03 100644 --- a/src/routes/ai.ts +++ b/src/routes/ai.ts @@ -1,37 +1,38 @@ import { Router } from "express"; + import { AiController } from "../controllers/AiController"; -import { aiEndpoint } from "../middleware/endpoints/aiEndpoint"; +import { timerStop } from "../middleware/analytics/perf/timer"; import { globalRateLimits } from "../middleware/auth/globalRateLimits"; -import { zodValidator } from "../middleware/sanity/validator"; -import { AiInputDataSchema } from "../types/ai"; -import { cleanup } from "../middleware/sanity/cleanup"; +import { leveretAuth } from "../middleware/auth/leveretAuth"; +import { userRateLimits } from "../middleware/auth/userRateLimits"; +import { aiEndpoint } from "../middleware/endpoints/aiEndpoint"; import { aiTokenChecker } from "../middleware/sanity/aiTokenChecker"; +import { cleanup } from "../middleware/sanity/cleanup"; import { filterBody } from "../middleware/sanity/filterBody"; -import { leveretAuth } from "../middleware/auth/leveretAuth"; import { jsonWithRawBody } from "../middleware/sanity/jsonWithRawBody"; -import { userRateLimits } from "../middleware/auth/userRateLimits"; -import { timerStop } from "../middleware/analytics/perf/timer"; +import { zodValidator } from "../middleware/sanity/validator"; +import { AiInputDataSchema } from "../types/ai"; export function aiRoutes() { - const router = Router(); - const controller = new AiController(); + const router = Router(); + const controller = new AiController(); - router.use(jsonWithRawBody()); - router.use(leveretAuth); + router.use(jsonWithRawBody()); + router.use(leveretAuth); - router.post( - "/:model/:systemPrompt", - aiEndpoint, - zodValidator(AiInputDataSchema), - globalRateLimits, - userRateLimits, - filterBody, - aiTokenChecker, - controller.prompt - ); + router.post( + "/:model/:systemPrompt", + aiEndpoint, + zodValidator(AiInputDataSchema), + globalRateLimits, + userRateLimits, + filterBody, + aiTokenChecker, + controller.prompt, + ); - router.use(timerStop); - router.use(cleanup); + router.use(timerStop); + router.use(cleanup); - return router; + return router; } diff --git a/src/routes/index.ts b/src/routes/index.ts index 4778403..6dce86c 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,20 +1,20 @@ import { Router } from "express"; -import { timerStart, timerStop } from "../middleware/analytics/perf/timer"; -import { cleanup } from "../middleware/sanity/cleanup"; + +import { timerStart } from "../middleware/analytics/perf/timer"; +import { loadEndpoint } from "../middleware/endpoints/loadEndpoint"; import { aiRoutes } from "./ai"; import { membersRoutes } from "./members"; import { oredicRoutes } from "./oredic"; -import { loadEndpoint } from "../middleware/endpoints/loadEndpoint"; export function routes() { - const router = Router(); + const router = Router(); - router.use(timerStart); - router.use(loadEndpoint); + router.use(timerStart); + router.use(loadEndpoint); - router.use("/ai", aiRoutes()); - router.use("/members", membersRoutes()); - router.use("/oredic", oredicRoutes()); + router.use("/ai", aiRoutes()); + router.use("/members", membersRoutes()); + router.use("/oredic", oredicRoutes()); - return router; + return router; } diff --git a/src/routes/members.ts b/src/routes/members.ts index f450f5f..cafa1a9 100644 --- a/src/routes/members.ts +++ b/src/routes/members.ts @@ -1,19 +1,20 @@ import { Router } from "express"; + import { MembersController } from "../controllers/MembersController"; +import { timerStop } from "../middleware/analytics/perf/timer"; import { membersEndpoint } from "../middleware/endpoints/membersEndpoint"; import { cleanup } from "../middleware/sanity/cleanup"; -import { timerStop } from "../middleware/analytics/perf/timer"; export function membersRoutes() { - const router = Router(); - const controller = new MembersController(); + const router = Router(); + const controller = new MembersController(); - router.get("/user-ids", membersEndpoint, controller.getIds); - router.get("/usernames", membersEndpoint, controller.getUsernames); - router.get("/users", membersEndpoint, controller.getUsers); + router.get("/user-ids", membersEndpoint, controller.getIds); + router.get("/usernames", membersEndpoint, controller.getUsernames); + router.get("/users", membersEndpoint, controller.getUsers); - router.use(timerStop); - router.use(cleanup); + router.use(timerStop); + router.use(cleanup); - return router; + return router; } diff --git a/src/routes/oredic.ts b/src/routes/oredic.ts index 45cc893..f31ad38 100644 --- a/src/routes/oredic.ts +++ b/src/routes/oredic.ts @@ -1,20 +1,21 @@ import { Router } from "express"; + import { OredicController } from "../controllers/OredicController"; +import { timerStop } from "../middleware/analytics/perf/timer"; import { oredicEndpoint } from "../middleware/endpoints/oredicEndpoint"; -import { jsonWithRawBody } from "../middleware/sanity/jsonWithRawBody"; import { cleanup } from "../middleware/sanity/cleanup"; -import { timerStop } from "../middleware/analytics/perf/timer"; +import { jsonWithRawBody } from "../middleware/sanity/jsonWithRawBody"; export function oredicRoutes() { - const router = Router(); - const controller = new OredicController(); + const router = Router(); + const controller = new OredicController(); - router.use(jsonWithRawBody()); + router.use(jsonWithRawBody()); - router.post("/:pack/:action", oredicEndpoint, controller.simplify); + router.post("/:pack/:action", oredicEndpoint, controller.simplify); - router.use(timerStop); - router.use(cleanup); + router.use(timerStop); + router.use(cleanup); - return router; + return router; } diff --git a/src/server.ts b/src/server.ts deleted file mode 100644 index 0ce0f01..0000000 --- a/src/server.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { initExpress } from "./express"; -import { getDiscordClient, initDiscordClient } from "./clients/DiscordClient"; -import { initGrokClient } from "./clients/GrokClient"; -import { getRedisClient, initRedisClient } from "./clients/RedisClient"; -import { config, env } from "./config/config"; -import { getDbHandler, initDbHandler } from "./db/DbHandler"; -import { LEVERET_PUBLIC_KEY } from "./loaders/keys"; -import { - startGlobalRateLimitIncrement, - startUserRateLimitIncrements, -} from "./services/rateLimits"; -import { getLogger, initLogger } from "./utils/Logger"; -import { startTimer, stopTimer } from "./utils/Timer"; -import { initDiscord } from "./discord"; - -async function main() { - startTimer("main"); - - initLogger(); - - getLogger().formattingLog("Clients Init"); - initRedisClient(); - await getRedisClient().connect(); - initGrokClient(); - - getLogger().formattingLog("DB Init"); - initDbHandler(); - await getDbHandler().init(); - - getLogger().formattingLog("Discord Bot Init"); - await initDiscord(); - - getLogger().formattingLog("Services Init"); - startGlobalRateLimitIncrement(); - await startUserRateLimitIncrements(); - - getLogger().formattingLog("App Init"); - const expressApp = initExpress(); - - expressApp.listen(config.PORT, config.RUNNING_IP, () => { - getLogger().formattingLog("Server Info"); - getLogger().simpleLog( - "info", - `Server is running at http://${config.RUNNING_IP}:${config.PORT}` - ); - getLogger().simpleLog( - "telemetry", - `Server took ${stopTimer("main").getTime("auto", 3).formatted} to start` - ); - getLogger().formattingLog("Server Ready"); - }); -} - -main(); diff --git a/src/services/ai/GrokService.ts b/src/services/ai/GrokService.ts index 8a11929..421a300 100644 --- a/src/services/ai/GrokService.ts +++ b/src/services/ai/GrokService.ts @@ -1,44 +1,29 @@ import { getGrokClient } from "../../clients/GrokClient"; import { config } from "../../config/config"; -import { AiModel, SystemPrompt } from "../../config/routes"; -import { AiInputData } from "../../types/ai"; -import { - DEFAULT_PROMPT, - HOGICHAN_PROMPT, - NOMICORD_PROMPT, -} from "../../loaders/storage"; +import type { AiModel, SystemPrompt } from "../../config/routes"; +import { DEFAULT_PROMPT, HOGICHAN_PROMPT, NOMICORD_PROMPT } from "../../loaders/storage"; +import type { AiInputData } from "../../types/ai"; import { formatCompletion } from "../../utils/grok/formatter"; const SYSTEM_PROMPTS = { - default: DEFAULT_PROMPT, - hogichan: HOGICHAN_PROMPT, - nomicord: NOMICORD_PROMPT, + default: DEFAULT_PROMPT, + hogichan: HOGICHAN_PROMPT, + nomicord: NOMICORD_PROMPT, } as const; export class GrokService { - async generateCompletion( - input: AiInputData, - model: AiModel, - systemPrompt: SystemPrompt - ) { - const modelConfig = config.ENDPOINTS[model]; - if (!modelConfig) { - throw new Error(`Model configuration not found for ${model}`); - } + async generateCompletion(input: AiInputData, model: AiModel, systemPrompt: SystemPrompt) { + const modelConfig = config.ENDPOINTS[model]; + if (!modelConfig) { + throw new Error(`Model configuration not found for ${model}`); + } - const completionParams = await formatCompletion( - input, - modelConfig.MODEL, - systemPrompt, - model - ); + const completionParams = await formatCompletion(input, modelConfig.MODEL, systemPrompt, model); - return await getGrokClient().client.chat.completions.create( - completionParams - ); - } + return getGrokClient().client.chat.completions.create(completionParams); + } - getSystemPrompt(type: SystemPrompt): string { - return SYSTEM_PROMPTS[type]; - } + getSystemPrompt(type: SystemPrompt): string { + return SYSTEM_PROMPTS[type]; + } } diff --git a/src/services/members/MembersService.ts b/src/services/members/MembersService.ts index 63faea8..094cd1d 100644 --- a/src/services/members/MembersService.ts +++ b/src/services/members/MembersService.ts @@ -2,31 +2,29 @@ import { getDbHandler } from "../../db/DbHandler"; import { findDcUsernameById } from "../../utils/bot/usernames"; export class MembersService { - private db = getDbHandler(); + private db = getDbHandler(); - async getIds(): Promise { - return this.db.getHogMembers(); - } + async getIds(): Promise { + return this.db.getHogMembers(); + } - async getUsernames(): Promise { - const ids = await this.db.getHogMembers(); - const usernames = await Promise.all( - ids.map((id) => findDcUsernameById(id)) - ); - return usernames.filter((name): name is string => name !== null); - } + async getUsernames(): Promise { + const ids = await this.db.getHogMembers(); + const usernames = await Promise.all(ids.map((id) => findDcUsernameById(id))); + return usernames.filter((name): name is string => name !== null); + } - async getUsers(): Promise> { - const ids = await this.db.getHogMembers(); - const result: Record = {}; + async getUsers(): Promise> { + const ids = await this.db.getHogMembers(); + const result: Record = {}; - await Promise.all( - ids.map(async (id) => { - const username = await findDcUsernameById(id); - result[id] = username || "unknown"; - }) - ); + await Promise.all( + ids.map(async (id) => { + const username = await findDcUsernameById(id); + result[id] = username || "unknown"; + }), + ); - return result; - } + return result; + } } diff --git a/src/services/oredic/OredicService.ts b/src/services/oredic/OredicService.ts index de25e32..25e2de8 100644 --- a/src/services/oredic/OredicService.ts +++ b/src/services/oredic/OredicService.ts @@ -1,103 +1,82 @@ -import { OredicParser } from "../../utils/parsers/OredicParser"; -import { OredicMatcher } from "../../utils/parsers/OredicMatcher"; -import { OredicPack } from "../../config/routes"; -import { AstNode, BuildOptions, OredicMatches } from "../../types/parsing"; -import { StandardError } from "../../types/errors"; +import type { OredicPack } from "../../config/routes"; +import type { StandardError } from "../../types/errors"; +import type { AstNode, BuildOptions, OredicMatches } from "../../types/parsing"; import { ErrorProne } from "../../utils/parentClasses/ErrorProne"; import { OredicBuilder } from "../../utils/parsers/OredicBuilder"; +import { OredicMatcher } from "../../utils/parsers/OredicMatcher"; +import { OredicParser } from "../../utils/parsers/OredicParser"; export class OredicService extends ErrorProne { - constructor(private pack: OredicPack) { - super("OredicService"); - } - - private parser = new OredicParser(this.pack); - private matcher = new OredicMatcher(this.pack); - private builder = new OredicBuilder(this.pack); - - async parse(input: string): Promise { - const parseResult = this.parser.parse(input); - if (!parseResult) { - return this.setError( - 500, - "Unknown Error: Could not parse input", - "parse" - ); - } - return parseResult; - } - - async match(ast: AstNode): Promise { - const matchResult = this.matcher.match(ast); - return matchResult; - } - - async buildAsts( - matches: OredicMatches, - options: BuildOptions - ): Promise { - const builtAst = this.builder.buildFromMatches(matches, options); - return builtAst; - } - - async buildSingleAst( - matches: OredicMatches, - options: BuildOptions - ): Promise { - let optionsCount = 0; - - for (const option in options) { - if (options[option as keyof BuildOptions] === true) { - optionsCount += 1; - } + constructor(private pack: OredicPack) { + super("OredicService"); } - if (optionsCount !== 1) { - return this.setError( - 400, - "Must select one and only one way to build the ast.", - "buildSingleAst" - ); - } + private parser = new OredicParser(this.pack); + private matcher = new OredicMatcher(this.pack); + private builder = new OredicBuilder(this.pack); - const builtAst = this.builder.buildFromMatches(matches, options); + async parse(input: string): Promise { + const parseResult = this.parser.parse(input); + if (!parseResult) { + return this.setError(500, "Unknown Error: Could not parse input", "parse"); + } + return parseResult; + } - if (this.isError(builtAst)) { - return this.propagateError( - builtAst, - "Failed to build AST", - "buildSingleAst" - ); + async match(ast: AstNode): Promise { + const matchResult = this.matcher.match(ast); + return matchResult; } - if (builtAst.length === 0) { - return this.setError(400, "No AST was built", "buildSingleAst"); + async buildAsts(matches: OredicMatches, options: BuildOptions): Promise { + const builtAst = this.builder.buildFromMatches(matches, options); + return builtAst; } - return builtAst[0]; - } + async buildSingleAst(matches: OredicMatches, options: BuildOptions): Promise { + let optionsCount = 0; + + for (const option in options) { + if (options[option as keyof BuildOptions] === true) { + optionsCount += 1; + } + } - async build(ast: AstNode): Promise { - return this.builder.buildFromAst(ast); - } + if (optionsCount !== 1) { + return this.setError(400, "Must select one and only one way to build the ast.", "buildSingleAst"); + } - async buildAndFindBest(asts: AstNode[]): Promise { - let candidates: string[] = []; + const builtAst = this.builder.buildFromMatches(matches, options); - for (const ast of asts) { - const candidate = this.builder.buildFromAst(ast); + if (this.isError(builtAst)) { + return this.propagateError(builtAst, "Failed to build AST", "buildSingleAst"); + } - if (this.isError(candidate)) { - return this.propagateError( - candidate, - "Failed to find best string from Ast" - ); - } + if (builtAst.length === 0) { + return this.setError(400, "No AST was built", "buildSingleAst"); + } - candidates.push(candidate); + return builtAst[0]; } - candidates = candidates.sort((a, b) => a.length - b.length); - return candidates[0]; - } + async build(ast: AstNode): Promise { + return this.builder.buildFromAst(ast); + } + + async buildAndFindBest(asts: AstNode[]): Promise { + let candidates: string[] = []; + + for (const ast of asts) { + const candidate = this.builder.buildFromAst(ast); + + if (this.isError(candidate)) { + return this.propagateError(candidate, "Failed to find best string from Ast"); + } + + candidates.push(candidate); + } + + candidates = candidates.sort((a, b) => a.length - b.length); + return candidates[0]; + } } diff --git a/src/services/rateLimits.ts b/src/services/rateLimits.ts index 7426836..67fb0a0 100644 --- a/src/services/rateLimits.ts +++ b/src/services/rateLimits.ts @@ -1,103 +1,86 @@ import { config } from "../config/config"; import { getDbHandler } from "../db/DbHandler"; -import { getRedisClient } from "../clients/RedisClient"; -import { getLogger } from "../utils/Logger"; +import { getLogger } from "../helpers/Logger"; const rateLimitIntervals: Map = new Map(); export function startGlobalRateLimitIncrement() { - for (const [endpointName, endpointConfig] of Object.entries( - config.ENDPOINTS - )) { - if (rateLimitIntervals.has(`${endpointName}:global`)) continue; + for (const [endpointName, endpointConfig] of Object.entries(config.ENDPOINTS)) { + if (rateLimitIntervals.has(`${endpointName}:global`)) continue; - const intervalMs = endpointConfig.RATE_LIMIT.GLOBAL.INTERVAL * 1000; + const intervalMs = endpointConfig.RATE_LIMIT.GLOBAL.INTERVAL * 1000; - const interval = setInterval(async () => { - try { - await getDbHandler().updateGlobalRates(endpointName, "give"); - } catch (err) { - getLogger().simpleLog( - "error", - `Failed to increment global rates for ${endpointName}: ${err}` - ); - } - }, intervalMs); + const interval = setInterval(async () => { + try { + await getDbHandler().updateGlobalRates(endpointName, "give"); + } catch (err) { + getLogger().simpleLog( + "error", + `Failed to increment global rates for ${endpointName}: ${err}`, + ); + } + }, intervalMs); - rateLimitIntervals.set(`${endpointName}:global`, interval); + rateLimitIntervals.set(`${endpointName}:global`, interval); - getLogger().simpleLog( - "success", - `Global rate limit increment started for ${endpointName} (every ${endpointConfig.RATE_LIMIT.GLOBAL.INTERVAL}s)` - ); - } + getLogger().simpleLog( + "success", + `Global rate limit increment started for ${endpointName} (every ${endpointConfig.RATE_LIMIT.GLOBAL.INTERVAL}s)`, + ); + } } export async function startUserRateLimitIncrements() { - for (const [endpointName, endpointConfig] of Object.entries( - config.ENDPOINTS - )) { - if (rateLimitIntervals.has(`${endpointName}:user`)) continue; + for (const [endpointName, endpointConfig] of Object.entries(config.ENDPOINTS)) { + if (rateLimitIntervals.has(`${endpointName}:user`)) continue; - const intervalMs = endpointConfig.RATE_LIMIT.USER.INTERVAL * 1000; + const intervalMs = endpointConfig.RATE_LIMIT.USER.INTERVAL * 1000; - const interval = setInterval(async () => { - try { - const keys = await getDbHandler().getAllUserKeysForEndpoint( - endpointName - ); + const interval = setInterval(async () => { + try { + const keys = await getDbHandler().getAllUserKeysForEndpoint(endpointName); - for (const key of keys) { - // Extract userId from key: "endpointName:user:userId" - const userId = key.split(":")[2]; - if (!userId) continue; + for (const key of keys) { + // Extract userId from key: "endpointName:user:userId" + const userId = key.split(":")[2]; + if (!userId) continue; - if (endpointConfig.RATE_LIMIT.USER.WHITELIST.includes(userId)) - continue; + if (endpointConfig.RATE_LIMIT.USER.WHITELIST.includes(userId)) continue; - await getDbHandler().updateUserRates(userId, endpointName, "give"); - } - } catch (err) { - getLogger().simpleLog( - "error", - `Failed to increment user rates for ${endpointName}: ${err}` - ); - } - }, intervalMs); + await getDbHandler().updateUserRates(userId, endpointName, "give"); + } + } catch (err) { + getLogger().simpleLog("error", `Failed to increment user rates for ${endpointName}: ${err}`); + } + }, intervalMs); - rateLimitIntervals.set(`${endpointName}:user`, interval); + rateLimitIntervals.set(`${endpointName}:user`, interval); - getLogger().simpleLog( - "success", - `User rate limit increment started for ${endpointName} (every ${endpointConfig.RATE_LIMIT.USER.INTERVAL}s)` - ); - } + getLogger().simpleLog( + "success", + `User rate limit increment started for ${endpointName} (every ${endpointConfig.RATE_LIMIT.USER.INTERVAL}s)`, + ); + } } export function stopGlobalRateLimitIncrement() { - for (const [key, interval] of rateLimitIntervals.entries()) { - if (key.endsWith(":global")) { - clearInterval(interval); - rateLimitIntervals.delete(key); - const endpointName = key.replace(":global", ""); - getLogger().simpleLog( - "info", - `Global rate limit increment stopped for ${endpointName}` - ); + for (const [key, interval] of rateLimitIntervals.entries()) { + if (key.endsWith(":global")) { + clearInterval(interval); + rateLimitIntervals.delete(key); + const endpointName = key.replace(":global", ""); + getLogger().simpleLog("info", `Global rate limit increment stopped for ${endpointName}`); + } } - } } export function stopUserRateLimitIncrements() { - for (const [key, interval] of rateLimitIntervals.entries()) { - if (key.endsWith(":user")) { - clearInterval(interval); - rateLimitIntervals.delete(key); - const endpointName = key.replace(":user", ""); - getLogger().simpleLog( - "info", - `User rate limit increments stopped for ${endpointName}` - ); + for (const [key, interval] of rateLimitIntervals.entries()) { + if (key.endsWith(":user")) { + clearInterval(interval); + rateLimitIntervals.delete(key); + const endpointName = key.replace(":user", ""); + getLogger().simpleLog("info", `User rate limit increments stopped for ${endpointName}`); + } } - } } diff --git a/src/types/ai.ts b/src/types/ai.ts index a900ca8..38e0d27 100644 --- a/src/types/ai.ts +++ b/src/types/ai.ts @@ -1,25 +1,22 @@ import { z } from "zod"; export const AiInputDataSchema = z.object({ - userId: z.string().min(1, "userId cannot be empty"), - prompt: z - .string() - .min(1, "prompt cannot be empty") - .max(10000, "prompt too long"), - context: z.string().max(5000, "context too long"), - attachment: z.url("attachment must be a valid URL").nullable(), + userId: z.string().min(1, "userId cannot be empty"), + prompt: z.string().min(1, "prompt cannot be empty").max(10000, "prompt too long"), + context: z.string().max(5000, "context too long"), + attachment: z.url("attachment must be a valid URL").nullable(), }); export type AiInputData = z.infer; export type ModelChoice = "grok-3-mini" | "grok-4-0709"; const GrokTokenizeReqSchema = z.object({ - token_ids: z.array( - z.object({ - token_id: z.number(), - string_token: z.string(), - token_bytes: z.array(z.number()), - }) - ), + token_ids: z.array( + z.object({ + token_id: z.number(), + string_token: z.string(), + token_bytes: z.array(z.number()), + }), + ), }); export type GrokTokenizeReq = z.infer; diff --git a/src/types/config.ts b/src/types/config.ts new file mode 100644 index 0000000..c3510ce --- /dev/null +++ b/src/types/config.ts @@ -0,0 +1,3 @@ +import type { ConfigSchema, FiltersSchema } from "../config/fileSchema"; + +export type ValidConfigSchema = ConfigSchema | FiltersSchema; diff --git a/src/types/errors.ts b/src/types/errors.ts index f463f2d..16ae13d 100644 --- a/src/types/errors.ts +++ b/src/types/errors.ts @@ -1,13 +1,13 @@ -import { PathLike } from "fs"; +import type { PathLike } from "fs"; export type StandardError = { - type: "error"; - code: number | null; - status: boolean; - send: boolean; - message: string | null; - location: PathLike | null; - time: Date | null; - context?: string | null; // Method/class context where error occurred - stackTrace?: StandardError[]; // Chain of errors from deepest to shallowest + type: "error"; + code: number | null; + status: boolean; + send: boolean; + message: string | null; + location: PathLike | null; + time: Date | null; + context?: string | null; // Method/class context where error occurred + stackTrace?: StandardError[]; // Chain of errors from deepest to shallowest }; diff --git a/src/types/express.ts b/src/types/express.ts index 612ca6e..741cd94 100644 --- a/src/types/express.ts +++ b/src/types/express.ts @@ -1,79 +1,73 @@ -import { - AiModel, - SystemPrompt, - OredicPack, - OredicAction, -} from "../config/routes"; -import { TimerRes } from "./timer"; -import { ChatCompletion } from "openai/resources/index"; -import { AstNode, OredicMatches } from "./parsing"; -import { Request } from "express"; +import type { Request } from "express"; +import type { ChatCompletion } from "openai/resources/index"; + +import type { AiModel, OredicAction, OredicPack, SystemPrompt } from "../config/routes"; +import type { AstNode, OredicMatches } from "./parsing"; +import type { TimerRes } from "./timer"; declare global { - namespace Express { - interface Request { - timerId: string; - endpoint?: string; - flags?: { - isLeveret?: boolean; - }; - ai?: { - model?: AiModel; - systemPrompt?: SystemPrompt; - }; - oredic?: { - pack?: OredicPack; - action?: OredicAction; - }; + namespace Express { + interface Request { + timerId: string; + endpoint?: string; + flags?: { + isLeveret?: boolean; + }; + ai?: { + model?: AiModel; + systemPrompt?: SystemPrompt; + }; + oredic?: { + pack?: OredicPack; + action?: OredicAction; + }; + } } - } } declare global { - namespace Express { - interface Response { - data: AiData | MembersData | OredicData; - timing: TimerRes; + namespace Express { + interface Response { + data: AiData | MembersData | OredicData; + timing: TimerRes; + } } - } } export type AiData = ChatCompletion; export type MembersData = { - users?: Record; - ids?: string[]; - usernames?: string[]; + users?: Record; + ids?: string[]; + usernames?: string[]; }; export type OredicData = { - ast?: AstNode; - matches?: OredicMatches; - bestFilter?: { - ast: AstNode; - string: string; - }; + ast?: AstNode; + matches?: OredicMatches; + bestFilter?: { + ast: AstNode; + string: string; + }; }; // Request type helpers for controllers export type OredicRequestBody = { - filter?: string; - ast?: AstNode; - matches?: string[]; + filter?: string; + ast?: AstNode; + matches?: string[]; }; export type OredicRequest = Request<{}, any, OredicRequestBody> & { - oredic: { - pack: OredicPack; - action: OredicAction; - }; + oredic: { + pack: OredicPack; + action: OredicAction; + }; }; -export function assertOredicRequest( - req: Request -): asserts req is OredicRequest { - const r = req as any; - if (!r.oredic?.pack || !r.oredic?.action) { - throw new Error("Invalid request: missing oredic context"); - } +export function assertOredicRequest(req: Request): asserts req is OredicRequest { + const r = req as any; + if (!r.oredic?.pack || !r.oredic?.action) { + throw new Error("Invalid request: missing oredic context"); + } } export {}; diff --git a/src/types/parsing.ts b/src/types/parsing.ts index c2a12ba..be2fd7c 100644 --- a/src/types/parsing.ts +++ b/src/types/parsing.ts @@ -1,147 +1,133 @@ export enum OperatorChar { - AND = "&", - OR = "|", - XOR = "^", + AND = "&", + OR = "|", + XOR = "^", } export enum SpecialChar { - WILDCARD = "*", - NEGATION = "!", - GROUP_START = "(", - GROUP_END = ")", - SPACE = " ", + WILDCARD = "*", + NEGATION = "!", + GROUP_START = "(", + GROUP_END = ")", + SPACE = " ", } export enum LexemeNames { - OPERATOR = "operator", - WILDCARD = "wildcard", - NEGATION = "negation", - GROUP = "group", - TEXT = "text", + OPERATOR = "operator", + WILDCARD = "wildcard", + NEGATION = "negation", + GROUP = "group", + TEXT = "text", } export enum NodeNames { - OPERATOR = "operator", - PATTERN = "pattern", - REGEX = "regex", - OREDIC = "oredic", - TEXT = "text", - WILDCARD = "wildcard", + OPERATOR = "operator", + PATTERN = "pattern", + REGEX = "regex", + OREDIC = "oredic", + TEXT = "text", + WILDCARD = "wildcard", } export type AstNode = PatternNode | OperatorNode; -export type MatcherNode = - | RegexNode - | OredicNode - | MatcherOperatorNode - | AstNode; +export type MatcherNode = RegexNode | OredicNode | MatcherOperatorNode | AstNode; export type OperatorNode = AndNode | OrNode | XorNode; -export type MatcherOperatorNode = - | MatcherAndNode - | MatcherOrNode - | MatcherXorNode; - -export type LexemeElement = - | TextLexeme - | OperatorLexeme - | NegationLexeme - | WildcardLexeme - | GroupLexeme; +export type MatcherOperatorNode = MatcherAndNode | MatcherOrNode | MatcherXorNode; + +export type LexemeElement = TextLexeme | OperatorLexeme | NegationLexeme | WildcardLexeme | GroupLexeme; export type TextLexeme = { - type: LexemeNames.TEXT; - content: string; + type: LexemeNames.TEXT; + content: string; }; export type OperatorLexeme = { - type: LexemeNames.OPERATOR; - content: "AND" | "OR" | "XOR"; + type: LexemeNames.OPERATOR; + content: "AND" | "OR" | "XOR"; }; export type NegationLexeme = { - type: LexemeNames.NEGATION; - content: null; + type: LexemeNames.NEGATION; + content: null; }; export type WildcardLexeme = { - type: LexemeNames.WILDCARD; - content: null; + type: LexemeNames.WILDCARD; + content: null; }; export type GroupLexeme = { - type: LexemeNames.GROUP; - content: LexemeElement[]; + type: LexemeNames.GROUP; + content: LexemeElement[]; }; export type LexicalParseResult = { - lexemes: LexemeElement[]; - consumed: number; + lexemes: LexemeElement[]; + consumed: number; }; export type AndNode = { - type: NodeNames.OPERATOR; - operator: "AND"; - negation: boolean; - children: Array; + type: NodeNames.OPERATOR; + operator: "AND"; + negation: boolean; + children: Array; }; export type OrNode = { - type: NodeNames.OPERATOR; - operator: "OR"; - negation: boolean; - children: Array; + type: NodeNames.OPERATOR; + operator: "OR"; + negation: boolean; + children: Array; }; export type XorNode = { - type: NodeNames.OPERATOR; - operator: "XOR"; - negation: boolean; - children: Array; + type: NodeNames.OPERATOR; + operator: "XOR"; + negation: boolean; + children: Array; }; export type MatcherAndNode = { - type: NodeNames.OPERATOR; - operator: "AND"; - negation: boolean; - children: Array; + type: NodeNames.OPERATOR; + operator: "AND"; + negation: boolean; + children: Array; }; export type MatcherOrNode = { - type: NodeNames.OPERATOR; - operator: "OR"; - negation: boolean; - children: Array; + type: NodeNames.OPERATOR; + operator: "OR"; + negation: boolean; + children: Array; }; export type MatcherXorNode = { - type: NodeNames.OPERATOR; - operator: "XOR"; - negation: boolean; - children: Array; + type: NodeNames.OPERATOR; + operator: "XOR"; + negation: boolean; + children: Array; }; export type PatternNode = { - type: NodeNames.PATTERN; - negation: boolean; - children: Array; + type: NodeNames.PATTERN; + negation: boolean; + children: Array; }; export type RegexNode = { - type: NodeNames.REGEX; - negation: boolean; - rawChild: string; - child: RegExp; + type: NodeNames.REGEX; + negation: boolean; + rawChild: string; + child: RegExp; }; export type OredicNode = { - type: NodeNames.OREDIC; - negation: boolean; - children: string[]; + type: NodeNames.OREDIC; + negation: boolean; + children: string[]; }; -export type Token = - | { type: NodeNames.TEXT; content: string } - | { type: NodeNames.WILDCARD }; +export type Token = { type: NodeNames.TEXT; content: string } | { type: NodeNames.WILDCARD }; export type OredicMatches = string[]; export type BuildOptions = { - shortenedDisjunction: boolean; + shortenedDisjunction: boolean; }; diff --git a/src/types/server.ts b/src/types/server.ts index 41ed0da..881fa5e 100644 --- a/src/types/server.ts +++ b/src/types/server.ts @@ -1,22 +1,13 @@ -import { SystemPrompt } from "../config/routes"; -import { EndpointConfig as ConfigEndpointConfig } from "../config/schema"; - -export type LogType = - | "success" - | "info" - | "warn" - | "error" - | "debug" - | "telemetry"; +export type LogType = "success" | "info" | "warn" | "error" | "debug" | "telemetry"; export type Filter = { - pattern: RegExp; - severity: "critical" | "high" | "medium" | "low"; - description: string; + pattern: RegExp; + severity: "critical" | "high" | "medium" | "low"; + description: string; }; export type PatternConfig = { - pattern: string; - severity: "critical" | "high" | "medium" | "low"; - description: string; + pattern: string; + severity: "critical" | "high" | "medium" | "low"; + description: string; }; diff --git a/src/types/time/timer.ts b/src/types/time/timer.ts new file mode 100644 index 0000000..6c05e43 --- /dev/null +++ b/src/types/time/timer.ts @@ -0,0 +1,7 @@ +export type TimerResult = { + raw: number; + adjusted: number; + formatted: string; +}; + +export type TimeUnit = "micro" | "ms" | "s" | "m"; diff --git a/src/types/timer.ts b/src/types/timer.ts index b9301db..120344f 100644 --- a/src/types/timer.ts +++ b/src/types/timer.ts @@ -3,7 +3,7 @@ export type AdjustTime = number; export type FormattedTime = string; export type TimerRes = { - raw: number; - adjusted: number; - formatted: string; + raw: number; + adjusted: number; + formatted: string; }; diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts deleted file mode 100644 index 4333910..0000000 --- a/src/utils/Logger.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { config } from "../config/config"; -import { LogType } from "../types/server"; -import { queryTimer } from "./Timer"; - -let logger: Logger | null = null; - -enum AnsiColor { - ERROR = "\x1b[31m", - SUCCESS = "\x1b[32m", - WARN = "\x1b[33m", - TELEMETRY = "\x1b[34m", - DEBUG = "\x1b[35m", - INFO = "\x1b[36m", - FORMAT = "\x1b[37m", - RESET = "\x1b[0m", -} - -enum FormattingConstant { - MAX_TYPE_LENGTH = 9, // "success".length - PROGRESS_BAR_FILLED = "█", - PROGRESS_BAR_EMPTY = "░", - FORMATTING_DASH = "-", - FORMATTING_TARGET_WIDTH = 50, - SECONDS_PER_MINUTE = 60, - MILLISECONDS_PER_SECOND = 1000, -} - -export class Logger { - private name: string; - - constructor() { - this.name = config.LOGGER_NAME; - } - - simpleLog(type: LogType, message: string): void { - const timestamp = this.getCurrentTimestamp(); - const logPrefix = this.formatLogPrefix(type, timestamp); - const coloredMessage = this.colorize(message, type); - - console.log(`${logPrefix}: ${coloredMessage}`); - } - - formattingLog(title: string): void { - const timestamp = this.getCurrentTimestamp(); - const logPrefix = this.formatLogPrefix("format", timestamp); - const formattedTitle = this.formatTitle(title); - const coloredTitle = this.colorize(formattedTitle, "format"); - - console.log(`${logPrefix}: ${coloredTitle}`); - } - - progressBar( - currentCount: number, - totalCount: number, - timerId: string, - barWidth: number = 50, - label: string = "" - ): void { - const progress = this.buildProgressBar( - currentCount, - totalCount, - timerId, - barWidth, - label - ); - - const coloredProgress = this.colorize(progress, "info"); - process.stdout.write(`\r${coloredProgress}`); - - const isComplete = currentCount === totalCount; - if (isComplete) { - process.stdout.write("\n"); - } - } - - /* - * Formatting helpers - */ - - private formatLogPrefix(type: LogType | "format", timestamp: string): string { - const logName = `[${this.name}:${type.toUpperCase()}]`; - const logTimestamp = `[${timestamp}]`; - const padding = " ".repeat( - FormattingConstant.MAX_TYPE_LENGTH - type.length - ); - - return `${logName}${padding}@${logTimestamp}`; - } - - private formatTitle(title: string): string { - const dashCount = - (FormattingConstant.FORMATTING_TARGET_WIDTH - title.length) / 2; - const dashes = (FormattingConstant.FORMATTING_DASH as string).repeat( - dashCount - ); - const hasOddLength = dashCount % 1 !== 0; - const extraDash = hasOddLength ? FormattingConstant.FORMATTING_DASH : ""; - - return `|${dashes} ${title} ${dashes}${extraDash}|`; - } - - private buildProgressBar( - currentCount: number, - totalCount: number, - timerId: string, - barWidth: number, - label: string - ): string { - const percentage = Math.floor((currentCount / totalCount) * 100); - const bar = this.createBar(currentCount, totalCount, barWidth); - const timeInfo = this.createTimeInfo(currentCount, totalCount, timerId); - const labelText = label ? ` ${label}` : ""; - - return `[${bar}] ${percentage}% (${currentCount}/${totalCount})${labelText} ${timeInfo}`; - } - - private createBar( - currentCount: number, - totalCount: number, - barWidth: number - ): string { - const filledWidth = Math.floor((currentCount / totalCount) * barWidth); - const emptyWidth = Math.max(0, barWidth - filledWidth); - - const filled = (FormattingConstant.PROGRESS_BAR_FILLED as string).repeat( - filledWidth - ); - const empty = (FormattingConstant.PROGRESS_BAR_EMPTY as string).repeat( - emptyWidth - ); - - return `${filled}${empty}`; - } - - private createTimeInfo( - currentCount: number, - totalCount: number, - timerId: string - ): string { - const elapsedMilliseconds = queryTimer(timerId).getTime("ms").raw; - const elapsedSeconds = Math.floor( - elapsedMilliseconds / FormattingConstant.MILLISECONDS_PER_SECOND - ); - const elapsedFormatted = this.formatTime(elapsedSeconds); - - let etaText = ""; - const isInProgress = currentCount < totalCount && currentCount > 0; - - if (isInProgress) { - const remainingMilliseconds = - (elapsedMilliseconds / currentCount) * (totalCount - currentCount); - const remainingSeconds = Math.floor( - remainingMilliseconds / FormattingConstant.MILLISECONDS_PER_SECOND - ); - const etaFormatted = this.formatTime(remainingSeconds); - etaText = ` ETA: ${etaFormatted}`; - } - - return `[${elapsedFormatted}]${etaText}`; - } - - private formatTime(totalSeconds: number): string { - const minutes = Math.floor( - totalSeconds / FormattingConstant.SECONDS_PER_MINUTE - ); - const seconds = totalSeconds % FormattingConstant.SECONDS_PER_MINUTE; - const paddedSeconds = seconds.toString().padStart(2, "0"); - - return `${minutes}:${paddedSeconds}`; - } - - private colorize(text: string, type: LogType | "format"): string { - const colorMap: Record = { - success: AnsiColor.SUCCESS, - info: AnsiColor.INFO, - warn: AnsiColor.WARN, - error: AnsiColor.ERROR, - debug: AnsiColor.DEBUG, - telemetry: AnsiColor.TELEMETRY, - format: AnsiColor.FORMAT, - }; - - const color = colorMap[type]; - return `${color}${text}${AnsiColor.RESET}`; - } - - private getCurrentTimestamp(): string { - return new Date().toISOString(); - } -} - -export function initLogger(): Logger { - if (logger) return logger; - logger = new Logger(); - return logger; -} - -export function getLogger(): Logger { - if (!logger) - throw new Error("Logger not initialized. Call initLogger() first."); - return logger; -} diff --git a/src/utils/StringUtils.ts b/src/utils/StringUtils.ts new file mode 100644 index 0000000..ef3f848 --- /dev/null +++ b/src/utils/StringUtils.ts @@ -0,0 +1,71 @@ +import type fs from "fs"; + +export class StringUtils { + static getUtf8Bytes(string: string): number { + return new TextEncoder().encode(string).length; + } + + static toUtf8Decimals(string: string): number[] { + const out: number[] = []; + for (let i = 0; i < string.length; i++) { + const unicode = string.charCodeAt(i); + out.push(unicode); + } + return out; + } + + static isValidFileName(path: fs.PathLike): boolean { + const fileName = path.toString(); + + const byteLength = this.getUtf8Bytes(fileName); + if (byteLength > 255) { + return false; + } + + const unicodes: number[] = this.toUtf8Decimals(fileName); + for (const unicode of unicodes) { + if (unicode === 0 || unicode === 47) { + return false; + } + } + + if (unicodes.length === 0) return false; + + if (unicodes.length === 1 && unicodes[0] === 46) { + return false; + } + if (unicodes.length === 2 && unicodes[0] === 46 && unicodes[1] === 46) { + return false; + } + + return true; + } + + /* This enforces relative paths only, without dots to signify it is relative + * No paths beginning with / are allowed. + * This is good because npm always has some ambiguity as to where ./ is. + * This codebase makes all file operations through FileSystem, + * so paths should always be relative to the configured root directory. + */ + static isValidPath(path: fs.PathLike) { + let pathStr = path.toString(); + + if (pathStr.length === 0) return false; + + if (pathStr.startsWith("/")) return false; + + if (pathStr.endsWith("/")) { + pathStr = pathStr.substring(0, pathStr.length - 1); + } + + const elements = pathStr.split("/"); + + for (const element of elements) { + if (element === "") return false; + + if (!this.isValidFileName(element)) return false; + } + + return true; + } +} diff --git a/src/utils/Timer.ts b/src/utils/Timer.ts deleted file mode 100644 index 5bb90ff..0000000 --- a/src/utils/Timer.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { TimerRes } from "../types/timer"; - -let timers: Map = new Map(); - -export class Timer { - startTime: number; - - constructor() { - this.startTime = performance.now(); - } - - getTime( - unit: "micro" | "ms" | "s" | "m" | "auto" = "auto", - precision: number = 2 - ): TimerRes { - const timeTaken_ms = performance.now() - this.startTime; - let adjustedTime: number = 0; - let unitLabel: string = ""; - - switch (unit) { - case "auto": - if (timeTaken_ms < 1) { - adjustedTime = timeTaken_ms * 1000; - unitLabel = "μs"; - } else if (timeTaken_ms < 1000) { - adjustedTime = timeTaken_ms; - unitLabel = "ms"; - } else if (timeTaken_ms < 60000) { - adjustedTime = timeTaken_ms / 1000; - unitLabel = "s"; - } else { - adjustedTime = timeTaken_ms / 60000; - unitLabel = "m"; - } - break; - - case "micro": - adjustedTime = timeTaken_ms * 1000; - unitLabel = "μs"; - break; - - case "ms": - adjustedTime = timeTaken_ms; - unitLabel = "ms"; - break; - - case "s": - adjustedTime = timeTaken_ms / 1000; - unitLabel = "s"; - break; - - case "m": - adjustedTime = timeTaken_ms / 60000; - unitLabel = "m"; - break; - } - - const formattedTime = `${adjustedTime.toFixed(precision)}${unitLabel}`; - - return { - raw: timeTaken_ms, - adjusted: adjustedTime, - formatted: formattedTime, - }; - } -} - -export function startTimer(id: string): void { - if (!timers.get(id)) timers.set(id, new Timer()); -} - -export function stopTimer(id: string): Timer { - const time = timers.get(id); - if (!time) - throw new Error( - `Timer ${id} not initialized. Call startTimer(id:string) first.` - ); - timers.delete(id); - return time; -} - -export function queryTimer(id: string): Timer { - const time = timers.get(id); - if (!time) - throw new Error( - `Timer ${id} not initialized. Call startTimer(id:string) first.` - ); - return time; -} - -export function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} diff --git a/src/utils/bot/usernames.ts b/src/utils/bot/usernames.ts index 2f402b1..80e4032 100644 --- a/src/utils/bot/usernames.ts +++ b/src/utils/bot/usernames.ts @@ -1,15 +1,11 @@ -import { User } from "discord.js"; import { getDiscordClient } from "../../clients/DiscordClient"; -import { string } from "zod"; -export async function findDcIdByUsername( - dcUsername: string -): Promise { - const user = await getDiscordClient().client.users.fetch(dcUsername); - return user?.id ? user.id : null; +export async function findDcIdByUsername(dcUsername: string): Promise { + const user = await getDiscordClient().client.users.fetch(dcUsername); + return user?.id ? user.id : null; } export async function findDcUsernameById(dcId: string): Promise { - const user = await getDiscordClient().client.users.fetch(dcId); - return user?.username ? user.username : null; + const user = await getDiscordClient().client.users.fetch(dcId); + return user?.username ? user.username : null; } diff --git a/src/utils/grok/formatter.ts b/src/utils/grok/formatter.ts index 2a3c1b0..1bbb779 100644 --- a/src/utils/grok/formatter.ts +++ b/src/utils/grok/formatter.ts @@ -1,58 +1,50 @@ -import { ChatCompletionCreateParamsNonStreaming } from "openai/resources/chat/completions"; -import { AiInputData } from "../../types/ai"; -import { SystemPrompt } from "../../config/routes"; -import { findDcUsernameById } from "../bot/usernames"; +import type { ChatCompletionCreateParamsNonStreaming } from "openai/resources/chat/completions"; + +import type { SystemPrompt } from "../../config/routes"; import { getDbHandler } from "../../db/DbHandler"; -import { - DEFAULT_PROMPT, - HOGICHAN_PROMPT, - NOMICORD_PROMPT, -} from "../../loaders/storage"; +import { DEFAULT_PROMPT, HOGICHAN_PROMPT, NOMICORD_PROMPT } from "../../loaders/storage"; +import type { AiInputData } from "../../types/ai"; const SYSTEM_PROMPTS = { - default: DEFAULT_PROMPT, - hogichan: HOGICHAN_PROMPT, - nomicord: NOMICORD_PROMPT, + default: DEFAULT_PROMPT, + hogichan: HOGICHAN_PROMPT, + nomicord: NOMICORD_PROMPT, } as const; -export function formatQuestion( - discordUsername: string, - rawQuestion: string, - rawContext: string -) { - const prefix = `Question by :`; - const question = filter(rawQuestion, discordUsername); - const context = filter(rawContext, discordUsername); - const body = `Context:\n${context}\n\nQuestion:\n${question}`; - return `${prefix}\n${body}`; +export function formatQuestion(discordUsername: string, rawQuestion: string, rawContext: string) { + const prefix = `Question by :`; + const question = filter(rawQuestion, discordUsername); + const context = filter(rawContext, discordUsername); + const body = `Context:\n${context}\n\nQuestion:\n${question}`; + return `${prefix}\n${body}`; } function filter(text: string, discordUsername: string) { - return text; + return text; } export async function formatCompletion( - reqBody: AiInputData, - model: string, - type: SystemPrompt, - modelName: string + reqBody: AiInputData, + model: string, + type: SystemPrompt, + modelName: string, ): Promise { - const completion: ChatCompletionCreateParamsNonStreaming = { - model: model, - messages: [ - { - role: "system", - content: SYSTEM_PROMPTS[type], - }, - { - role: "user", - content: formatQuestion( - await getDbHandler().getUsername(reqBody.userId, modelName), - reqBody.prompt, - reqBody.context - ), - }, - ], - }; - return completion; + const completion: ChatCompletionCreateParamsNonStreaming = { + model: model, + messages: [ + { + role: "system", + content: SYSTEM_PROMPTS[type], + }, + { + role: "user", + content: formatQuestion( + await getDbHandler().getUsername(reqBody.userId, modelName), + reqBody.prompt, + reqBody.context, + ), + }, + ], + }; + return completion; } diff --git a/src/utils/grok/grok.ts b/src/utils/grok/grok.ts index 7ebcd84..3b30917 100644 --- a/src/utils/grok/grok.ts +++ b/src/utils/grok/grok.ts @@ -1,48 +1,42 @@ import { getGrokClient } from "../../clients/GrokClient"; import { env } from "../../config/config"; -import { GrokTokenizeReq } from "../../types/ai"; -import { getLogger } from "../Logger"; +import { getLogger } from "../../helpers/Logger"; +import type { GrokTokenizeReq } from "../../types/ai"; export async function tokenizeGrok( - text: string, - model: string + text: string, + model: string, ): Promise<{ stringTokens: string[]; tokenCount: number } | null> { - const response = await makeTokenizeReq(text, model); - if (!response) { - return null; - } - return { - stringTokens: response.token_ids.map((token) => token.string_token), - tokenCount: response.token_ids.length, - }; + const response = await makeTokenizeReq(text, model); + if (!response) { + return null; + } + return { + stringTokens: response.token_ids.map((token) => token.string_token), + tokenCount: response.token_ids.length, + }; } -async function makeTokenizeReq( - text: string, - model: string -): Promise { - const response = await fetch( - getGrokClient().client.baseURL + "/tokenize-text", - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${env.GROK_API_KEY}`, - }, - body: JSON.stringify({ - model, - text: text, - }), - } - ); +async function makeTokenizeReq(text: string, model: string): Promise { + const response = await fetch(getGrokClient().client.baseURL + "/tokenize-text", { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${env.GROK_API_KEY}`, + }, + body: JSON.stringify({ + model, + text: text, + }), + }); - if (!response.ok) { - getLogger().simpleLog( - "error", - `Error tokenizing input with grok. ${response.status} ${response.statusText}` - ); - return null; - } + if (!response.ok) { + getLogger().simpleLog( + "error", + `Error tokenizing input with grok. ${response.status} ${response.statusText}`, + ); + return null; + } - return await response.json(); + return response.json(); } diff --git a/src/utils/parentClasses/ErrorProne.ts b/src/utils/parentClasses/ErrorProne.ts index a0ffe03..bf65ff3 100644 --- a/src/utils/parentClasses/ErrorProne.ts +++ b/src/utils/parentClasses/ErrorProne.ts @@ -1,100 +1,86 @@ -import { StandardError } from "../../types/errors"; +import type { StandardError } from "../../types/errors"; export class ErrorProne { - protected error: StandardError; - protected className: string; + protected error: StandardError; + protected className: string; - constructor(className?: string) { - this.className = className || this.constructor.name; - this.error = { - type: "error", - code: null, - status: false, - send: false, - message: null, - location: __dirname, - time: null, - context: null, - stackTrace: [], - }; - } + constructor(className?: string) { + this.className = className || this.constructor.name; + this.error = { + type: "error", + code: null, + status: false, + send: false, + message: null, + location: __dirname, + time: null, + context: null, + stackTrace: [], + }; + } - protected sendErrorToClient() { - return { - message: this.error.message ? this.error.message : "no message", - culprit: `Error in file: ${this.error.location}, from method: ${this.error.context ? this.error.context : "Uknown"}`, - time: this.error.time ? this.error.time : "Uknown", - stackTrace: this.error.stackTrace, - }; - } + protected sendErrorToClient() { + return { + message: this.error.message ? this.error.message : "no message", + culprit: `Error in file: ${this.error.location}, from method: ${this.error.context ? this.error.context : "Uknown"}`, + time: this.error.time ? this.error.time : "Uknown", + stackTrace: this.error.stackTrace, + }; + } - protected setError( - code: number, - message: string, - methodName?: string - ): StandardError { - this.error.code = code; - this.error.status = true; - this.error.send = true; - this.error.message = message; - this.error.time = new Date(); - this.error.context = methodName - ? `${this.className}.${methodName}` - : this.className; - this.error.stackTrace = []; + protected setError(code: number, message: string, methodName?: string): StandardError { + this.error.code = code; + this.error.status = true; + this.error.send = true; + this.error.message = message; + this.error.time = new Date(); + this.error.context = methodName ? `${this.className}.${methodName}` : this.className; + this.error.stackTrace = []; - return this.error; - } + return this.error; + } - protected setWarn(message: string, methodName?: string): StandardError { - this.error.code = 200; - this.error.status = true; - this.error.send = true; - this.error.message = message; - this.error.time = new Date(); - this.error.context = methodName - ? `${this.className}.${methodName}` - : this.className; - this.error.stackTrace = []; + protected setWarn(message: string, methodName?: string): StandardError { + this.error.code = 200; + this.error.status = true; + this.error.send = true; + this.error.message = message; + this.error.time = new Date(); + this.error.context = methodName ? `${this.className}.${methodName}` : this.className; + this.error.stackTrace = []; - return this.error; - } + return this.error; + } - protected propagateError( - childError: StandardError, - message: string, - methodName?: string - ): StandardError { - const context = methodName - ? `${this.className}.${methodName}` - : this.className; + protected propagateError(childError: StandardError, message: string, methodName?: string): StandardError { + const context = methodName ? `${this.className}.${methodName}` : this.className; - return { - type: "error", - code: childError.code, - status: true, - send: true, - message: message, - location: __dirname, - time: new Date(), - context: context, - stackTrace: [childError, ...(childError.stackTrace || [])], - }; - } + return { + type: "error", + code: childError.code, + status: true, + send: true, + message: message, + location: __dirname, + time: new Date(), + context: context, + stackTrace: [childError, ...(childError.stackTrace || [])], + }; + } - protected getErrorCode() { - const errorCode = this.error.code; - if (!errorCode) { - this.propagateError( - this.error, - "Could not even get error code... somehow? Defaulting to 500. If you see this, the API is fucked." - ); - return 500; + protected getErrorCode() { + const errorCode = this.error.code; + if (!errorCode) { + this.propagateError( + this.error, + "Could not even get error code... somehow? Defaulting to 500. If you see this, the API is fucked.", + ); + return 500; + } + return errorCode; } - return errorCode; - } - protected isError(value: any): value is StandardError { - return value && typeof value === "object" && value.type === "error"; - } + protected isError(value: any): value is StandardError { + return value && typeof value === "object" && value.type === "error"; + } } diff --git a/src/utils/parsers/OredicBuilder.ts b/src/utils/parsers/OredicBuilder.ts index 02050ff..3365e1c 100644 --- a/src/utils/parsers/OredicBuilder.ts +++ b/src/utils/parsers/OredicBuilder.ts @@ -1,169 +1,142 @@ -import { set } from "zod"; -import { OredicPack } from "../../config/routes"; -import { StandardError } from "../../types/errors"; -import { - AstNode, - BuildOptions, - OredicMatches, - NodeNames, - OperatorChar, - OrNode, - PatternNode, -} from "../../types/parsing"; +import type { OredicPack } from "../../config/routes"; +import type { StandardError } from "../../types/errors"; +import type { AstNode, BuildOptions, OredicMatches, OrNode, PatternNode } from "../../types/parsing"; +import { NodeNames, OperatorChar } from "../../types/parsing"; import { ErrorProne } from "../parentClasses/ErrorProne"; import { OredicParser } from "./OredicParser"; import { OredicShortener } from "./OredicShortener"; export class OredicBuilder extends ErrorProne { - private shortener: OredicShortener; - private parser: OredicParser; - private shortcuts: Map; + private shortener: OredicShortener; + private parser: OredicParser; + private shortcuts: Map; - constructor(private pack: OredicPack) { - super("OredicBuilder"); + constructor(private pack: OredicPack) { + super("OredicBuilder"); - this.shortener = new OredicShortener(pack); - this.parser = new OredicParser(pack); + this.shortener = new OredicShortener(pack); + this.parser = new OredicParser(pack); - const shortcuts = this.shortener.getShortcuts(); - if (this.isError(shortcuts)) { - throw new Error(`Failed to load shortcuts: ${shortcuts.message}`); - } - this.shortcuts = shortcuts; - } - - buildFromMatches( - matches: OredicMatches, - options: BuildOptions - ): AstNode[] | StandardError { - const candidates = new Set(); - const pipeline = this.buildPipeline(options); - - for (const buildStrategy of pipeline) { - const result = buildStrategy(matches); - - if (this.isError(result)) { - return this.propagateError( - result, - "Build strategy failed", - "buildFromMatches" - ); - } - if (result) { - candidates.add(result); - } + const shortcuts = this.shortener.getShortcuts(); + if (this.isError(shortcuts)) { + throw new Error(`Failed to load shortcuts: ${shortcuts.message}`); + } + this.shortcuts = shortcuts; } - if (candidates.size === 0) { - return this.setError( - 500, - "No valid AST candidates generated", - "buildFromMatches" - ); - } - return Array.from(candidates); - } + buildFromMatches(matches: OredicMatches, options: BuildOptions): AstNode[] | StandardError { + const candidates = new Set(); + const pipeline = this.buildPipeline(options); - buildFromAst(ast: AstNode): string | StandardError { - if (!ast) { - return ""; + for (const buildStrategy of pipeline) { + const result = buildStrategy(matches); + + if (this.isError(result)) { + return this.propagateError(result, "Build strategy failed", "buildFromMatches"); + } + if (result) { + candidates.add(result); + } + } + + if (candidates.size === 0) { + return this.setError(500, "No valid AST candidates generated", "buildFromMatches"); + } + return Array.from(candidates); } - switch (ast.type) { - case NodeNames.OPERATOR: - const char: OperatorChar = this.getOperatorChar(ast.operator); - const children: string[] = []; + buildFromAst(ast: AstNode): string | StandardError { + if (!ast) { + return ""; + } - for (let i = 0; i < ast.children.length; i++) { - const child: AstNode = ast.children[i]; - let subFilter = this.buildFromAst(child); + switch (ast.type) { + case NodeNames.OPERATOR: + const char: OperatorChar = this.getOperatorChar(ast.operator); + const children: string[] = []; + + for (let i = 0; i < ast.children.length; i++) { + const child: AstNode = ast.children[i]; + let subFilter = this.buildFromAst(child); + + if (typeof subFilter !== "string") return subFilter; + + if (child.type === NodeNames.OPERATOR) { + const needsParens = i > 0 && child.operator !== ast.operator; + if (needsParens) { + subFilter = `(${subFilter})`; + } + } + + if (child.negation) { + subFilter = `!${subFilter}`; + } + + children.push(subFilter); + } + return children.join(char); + + case NodeNames.PATTERN: + return ast.children + .map((token) => { + if (token.type === NodeNames.WILDCARD) { + return "*"; + } else if (token.type === NodeNames.TEXT) { + return token.content; + } + return ""; + }) + .join(""); + } + } - if (typeof subFilter !== "string") return subFilter; + private shortenedDisjuntion(matches: OredicMatches): OrNode | StandardError { + const children: PatternNode[] = []; + for (const match in matches) { + const candidate = this.shortcuts.get(match); + const rawPattern = candidate ? candidate : match; + const nodeCandidate = this.parser.parse(rawPattern); - if (child.type === NodeNames.OPERATOR) { - const needsParens = i > 0 && child.operator !== ast.operator; - if (needsParens) { - subFilter = `(${subFilter})`; + if (!nodeCandidate) { + continue; + } + if (this.isError(nodeCandidate)) { + return this.propagateError(nodeCandidate, "Failed to parse pattern", "shortenedDisjuntion"); + } + if (nodeCandidate.type === NodeNames.OPERATOR) { + return this.setError(500, "Parsed a pattern as an operator", "shortenedDisjuntion"); } - } - - if (child.negation) { - subFilter = `!${subFilter}`; - } - children.push(subFilter); + children.push(nodeCandidate); } - return children.join(char); - - case NodeNames.PATTERN: - return ast.children - .map((token) => { - if (token.type === NodeNames.WILDCARD) { - return "*"; - } else if (token.type === NodeNames.TEXT) { - return token.content; - } - return ""; - }) - .join(""); - } - } - - private shortenedDisjuntion(matches: OredicMatches): OrNode | StandardError { - const children: PatternNode[] = []; - for (const match in matches) { - const candidate = this.shortcuts.get(match); - const rawPattern = candidate ? candidate : match; - const nodeCandidate = this.parser.parse(rawPattern); - - if (!nodeCandidate) { - continue; - } - if (this.isError(nodeCandidate)) { - return this.propagateError( - nodeCandidate, - "Failed to parse pattern", - "shortenedDisjuntion" - ); - } - if (nodeCandidate.type === NodeNames.OPERATOR) { - return this.setError( - 500, - "Parsed a pattern as an operator", - "shortenedDisjuntion" - ); - } - - children.push(nodeCandidate); + + return { + type: NodeNames.OPERATOR, + operator: "OR", + negation: false, + children: children, + }; } - return { - type: NodeNames.OPERATOR, - operator: "OR", - negation: false, - children: children, - }; - } + private buildPipeline(options: BuildOptions) { + const pipeline = []; - private buildPipeline(options: BuildOptions) { - const pipeline = []; + if (options.shortenedDisjunction) { + pipeline.push(this.shortenedDisjuntion.bind(this)); + } - if (options.shortenedDisjunction) { - pipeline.push(this.shortenedDisjuntion.bind(this)); + return pipeline; } - return pipeline; - } - - private getOperatorChar(string: "AND" | "OR" | "XOR"): OperatorChar { - switch (string) { - case "AND": - return OperatorChar.AND; - case "OR": - return OperatorChar.OR; - case "XOR": - return OperatorChar.XOR; - default: - throw new Error(`Invalid operator string: ${string}`); + private getOperatorChar(string: "AND" | "OR" | "XOR"): OperatorChar { + switch (string) { + case "AND": + return OperatorChar.AND; + case "OR": + return OperatorChar.OR; + case "XOR": + return OperatorChar.XOR; + default: + throw new Error(`Invalid operator string: ${string}`); + } } - } } diff --git a/src/utils/parsers/OredicMatcher.ts b/src/utils/parsers/OredicMatcher.ts index d6f9b98..4493ef7 100644 --- a/src/utils/parsers/OredicMatcher.ts +++ b/src/utils/parsers/OredicMatcher.ts @@ -1,363 +1,353 @@ -import { OredicPack } from "../../config/routes"; +import type { OredicPack } from "../../config/routes"; import { DUMPS } from "../../loaders/storage"; -import { StandardError } from "../../types/errors"; -import { - AstNode, - RegexNode, - OredicNode, - PatternNode, - NodeNames, - MatcherNode, - MatcherAndNode, - MatcherOrNode, - MatcherXorNode, - OredicMatches, +import type { StandardError } from "../../types/errors"; +import type { + AstNode, + MatcherAndNode, + MatcherNode, + MatcherOrNode, + MatcherXorNode, + OredicMatches, + OredicNode, + PatternNode, + RegexNode, } from "../../types/parsing"; +import { NodeNames } from "../../types/parsing"; import { ErrorProne } from "../parentClasses/ErrorProne"; import { OredicParser } from "./OredicParser"; const EMPTY_OREDIC_NODE: OredicNode = { - type: NodeNames.OREDIC, - negation: false, - children: [], + type: NodeNames.OREDIC, + negation: false, + children: [], } as const; export class OredicMatcher extends ErrorProne { - private parser: OredicParser; - private dump: string[]; - - constructor(private pack: OredicPack) { - super("OredicMatcher"); - this.parser = new OredicParser(pack); - const dump = DUMPS.get(this.pack); - this.dump = dump - ? dump.split("\n").filter((line) => line.trim() !== "") - : []; - } - - match(rules: AstNode): OredicMatches | StandardError { - const result = this.parse(rules); - if (result === null) { - return this.setError(500, "Failed to match oredics", "match"); + private parser: OredicParser; + private dump: string[]; + + constructor(private pack: OredicPack) { + super("OredicMatcher"); + this.parser = new OredicParser(pack); + const dump = DUMPS.get(this.pack); + this.dump = dump ? dump.split("\n").filter((line) => line.trim() !== "") : []; } - return result; - } - isUniqueMatch(pattern: string, targetIndex: number): boolean { - if (targetIndex < 0 || targetIndex >= this.dump.length) { - return false; + match(rules: AstNode): OredicMatches | StandardError { + const result = this.parse(rules); + if (result === null) { + return this.setError(500, "Failed to match oredics", "match"); + } + return result; } - const target = this.dump[targetIndex]; - const regex = this.buildRegexFromPattern(pattern); + isUniqueMatch(pattern: string, targetIndex: number): boolean { + if (targetIndex < 0 || targetIndex >= this.dump.length) { + return false; + } - if (!regex.test(target)) { - return false; - } + const target = this.dump[targetIndex]; + const regex = this.buildRegexFromPattern(pattern); - const maxRadius = 128; - for (let radius = 1; radius <= maxRadius; radius *= 2) { - for (let offset = -radius; offset <= radius; offset++) { - const checkIndex = targetIndex + offset; - - if ( - checkIndex === targetIndex || - checkIndex < 0 || - checkIndex >= this.dump.length - ) { - continue; + if (!regex.test(target)) { + return false; } - regex.lastIndex = 0; - if (regex.test(this.dump[checkIndex])) { - return false; + const maxRadius = 128; + for (let radius = 1; radius <= maxRadius; radius *= 2) { + for (let offset = -radius; offset <= radius; offset++) { + const checkIndex = targetIndex + offset; + + if (checkIndex === targetIndex || checkIndex < 0 || checkIndex >= this.dump.length) { + continue; + } + + regex.lastIndex = 0; + if (regex.test(this.dump[checkIndex])) { + return false; + } + } } - } - } - for (let i = 0; i < this.dump.length; i++) { - const distance = Math.abs(i - targetIndex); + for (let i = 0; i < this.dump.length; i++) { + const distance = Math.abs(i - targetIndex); + + if (distance <= maxRadius || i === targetIndex) { + continue; + } - if (distance <= maxRadius || i === targetIndex) { - continue; - } + regex.lastIndex = 0; + if (regex.test(this.dump[i])) { + return false; + } + } - regex.lastIndex = 0; - if (regex.test(this.dump[i])) { - return false; - } + return true; } - return true; - } - - /* - * AST transformation pipeline - */ - - private parse(ast: MatcherNode | null): string[] | null { - this.parsePatterns(ast); - this.parseRegexes(ast); - const finalNode = this.parseOredics(ast); - if (!finalNode) return null; - return finalNode.children; - } - - private parsePatterns(ast: MatcherNode | null): void { - if (!ast || ast.type === "regex" || ast.type === "oredic") return; - - if (ast.type === "pattern") { - const regexNode = this.createRegex(ast); - delete (ast as any).children; - Object.assign(ast, regexNode); - return; + /* + * AST transformation pipeline + */ + + private parse(ast: MatcherNode | null): string[] | null { + this.parsePatterns(ast); + this.parseRegexes(ast); + const finalNode = this.parseOredics(ast); + if (!finalNode) return null; + return finalNode.children; } - for (const child of ast.children) { - this.parsePatterns(child); + private parsePatterns(ast: MatcherNode | null): void { + if (!ast || ast.type === "regex" || ast.type === "oredic") return; + + if (ast.type === "pattern") { + const regexNode = this.createRegex(ast); + delete (ast as any).children; + Object.assign(ast, regexNode); + return; + } + + for (const child of ast.children) { + this.parsePatterns(child); + } } - } - private parseRegexes(ast: MatcherNode | null): void { - if (!ast || ast.type === "pattern" || ast.type === "oredic") return; + private parseRegexes(ast: MatcherNode | null): void { + if (!ast || ast.type === "pattern" || ast.type === "oredic") return; - if (ast.type === "regex") { - const oredicNode = this.matchOredics(ast); + if (ast.type === "regex") { + const oredicNode = this.matchOredics(ast); - if (!oredicNode) return; // Error + if (!oredicNode) return; // Error - delete (ast as any).children; - delete (ast as any).child; - delete (ast as any).rawChild; - Object.assign(ast, oredicNode); - return; - } + delete (ast as any).children; + delete (ast as any).child; + delete (ast as any).rawChild; + Object.assign(ast, oredicNode); + return; + } - for (const child of ast.children) { - this.parseRegexes(child); + for (const child of ast.children) { + this.parseRegexes(child); + } } - } - private parseOredics(ast: MatcherNode | null): OredicNode | null { - if (!ast) return null; + private parseOredics(ast: MatcherNode | null): OredicNode | null { + if (!ast) return null; - if (ast.type === "oredic") return ast; + if (ast.type === "oredic") return ast; - if (ast.type !== "operator") { - this.setError( - 500, - "Unknown Error: Got something else than Oredic and Operator Nodes in the final parse", - "parseOredics" - ); - return null; - } + if (ast.type !== "operator") { + this.setError( + 500, + "Unknown Error: Got something else than Oredic and Operator Nodes in the final parse", + "parseOredics", + ); + return null; + } - if (ast.negation && (ast.operator === "AND" || ast.operator === "OR")) { - ast.operator = ast.operator === "AND" ? "OR" : "AND"; - ast.negation = false; - for (const child of ast.children) { - child.negation = !child.negation; - } - } + if (ast.negation && (ast.operator === "AND" || ast.operator === "OR")) { + ast.operator = ast.operator === "AND" ? "OR" : "AND"; + ast.negation = false; + for (const child of ast.children) { + child.negation = !child.negation; + } + } - for (let i = 0; i < ast.children.length; i++) { - const result = this.parseOredics(ast.children[i]); - if (result) { - ast.children[i] = result; - } - } + for (let i = 0; i < ast.children.length; i++) { + const result = this.parseOredics(ast.children[i]); + if (result) { + ast.children[i] = result; + } + } - let newNode: OredicNode | null = null; - switch (ast.operator) { - case "AND": - newNode = this.applyConjunction(ast); - break; - case "OR": - newNode = this.applyDisjunction(ast); - break; - case "XOR": - newNode = this.applyExclusiveDisjunction(ast); - break; - } + let newNode: OredicNode | null = null; + switch (ast.operator) { + case "AND": + newNode = this.applyConjunction(ast); + break; + case "OR": + newNode = this.applyDisjunction(ast); + break; + case "XOR": + newNode = this.applyExclusiveDisjunction(ast); + break; + } - if (newNode) { - delete (ast as any).operator; - Object.assign(ast, newNode); - return ast as unknown as OredicNode; - } + if (newNode) { + delete (ast as any).operator; + Object.assign(ast, newNode); + return ast as unknown as OredicNode; + } - return null; - } - - private createRegex(node: PatternNode): RegexNode { - let pattern = "^"; - for (const child of node.children) { - if (child.type === "wildcard") { - pattern += "[a-zA-Z]+"; - } else { - pattern += child.content; - } + return null; } - pattern += "$"; - return { - type: NodeNames.REGEX, - negation: node.negation, - rawChild: pattern, - child: new RegExp(pattern, "gm"), - }; - } - - private matchOredics(node: RegexNode): OredicNode | null { - const dump = DUMPS.get(this.pack); - if (!dump) { - return null; - // Error + + private createRegex(node: PatternNode): RegexNode { + let pattern = "^"; + for (const child of node.children) { + if (child.type === "wildcard") { + pattern += "[a-zA-Z]+"; + } else { + pattern += child.content; + } + } + pattern += "$"; + return { + type: NodeNames.REGEX, + negation: node.negation, + rawChild: pattern, + child: new RegExp(pattern, "gm"), + }; } - const oredicList = dump.match(node.child); - return this.newOredicNode(oredicList); - } - - private applyConjunction(node: MatcherAndNode): OredicNode { - const occurences = new Map(); - const toRemove = new Set(); - let newChildren: string[] = []; - let nonNegatedCount = 0; - - for (const child of node.children) { - if (child.type !== "oredic") { - this.setError( - 500, - "Unknown Error: Tried to apply conjunction on something else than an oredic node", - "applyConjunction" - ); - return this.newOredicNode(null); - } - - if (!child.children || child.children.length === 0) { - if (!child.negation) { - return this.newOredicNode(null); + + private matchOredics(node: RegexNode): OredicNode | null { + const dump = DUMPS.get(this.pack); + if (!dump) { + return null; + // Error } - continue; - } + const oredicList = dump.match(node.child); + return this.newOredicNode(oredicList); + } - if (child.negation) { - for (const oredic of child.children) { - toRemove.add(oredic); + private applyConjunction(node: MatcherAndNode): OredicNode { + const occurences = new Map(); + const toRemove = new Set(); + const newChildren: string[] = []; + let nonNegatedCount = 0; + + for (const child of node.children) { + if (child.type !== "oredic") { + this.setError( + 500, + "Unknown Error: Tried to apply conjunction on something else than an oredic node", + "applyConjunction", + ); + return this.newOredicNode(null); + } + + if (!child.children || child.children.length === 0) { + if (!child.negation) { + return this.newOredicNode(null); + } + continue; + } + + if (child.negation) { + for (const oredic of child.children) { + toRemove.add(oredic); + } + } else { + nonNegatedCount++; + for (const oredic of child.children) { + const currentCount = occurences.get(oredic) ?? 0; + occurences.set(oredic, currentCount + 1); + } + } } - } else { - nonNegatedCount++; - for (const oredic of child.children) { - const currentCount = occurences.get(oredic) ?? 0; - occurences.set(oredic, currentCount + 1); + + if (nonNegatedCount === 0) { + this.setError(500, "AND operation requires at least one non-negated child", "applyConjunction"); + return this.newOredicNode(null); } - } - } - if (nonNegatedCount === 0) { - this.setError( - 500, - "AND operation requires at least one non-negated child", - "applyConjunction" - ); - return this.newOredicNode(null); + occurences.forEach((value, key) => { + if (value === nonNegatedCount && !toRemove.has(key)) { + newChildren.push(key); + } + }); + + return this.newOredicNode(newChildren); } - occurences.forEach((value, key) => { - if (value === nonNegatedCount && !toRemove.has(key)) { - newChildren.push(key); - } - }); - - return this.newOredicNode(newChildren); - } - - private applyDisjunction(node: MatcherOrNode): OredicNode { - let newChildren: string[] = []; - - for (const child of node.children) { - if (child.type !== "oredic") { - this.setError( - 500, - "Unknown Error: Tried to apply disjunction on something else than an oredic node", - "applyDisjunction" - ); - return this.newOredicNode(null); - } - - if (!child.negation) { - const toConcat = child.children ? child.children : []; - newChildren = newChildren.concat(toConcat); - } + private applyDisjunction(node: MatcherOrNode): OredicNode { + let newChildren: string[] = []; + + for (const child of node.children) { + if (child.type !== "oredic") { + this.setError( + 500, + "Unknown Error: Tried to apply disjunction on something else than an oredic node", + "applyDisjunction", + ); + return this.newOredicNode(null); + } + + if (!child.negation) { + const toConcat = child.children ? child.children : []; + newChildren = newChildren.concat(toConcat); + } + } + + return this.newOredicNode(newChildren); } - return this.newOredicNode(newChildren); - } - - private applyExclusiveDisjunction(node: MatcherXorNode): OredicNode { - const occurences = new Map(); - let newChildren: string[] = []; - - for (const child of node.children) { - if (child.type !== "oredic") { - this.setError( - 500, - "Unknown Error: Tried to apply xor on something else than an oredic node", - "applyExclusiveDisjunction" - ); - return this.newOredicNode(null); - } - - if (!child.children || child.children.length === 0) { - continue; - } - - const multiplier = child.negation ? -1 : 1; - - for (const oredic of child.children) { - const currentCount = occurences.get(oredic) ?? 0; - occurences.set(oredic, currentCount + multiplier); - } + private applyExclusiveDisjunction(node: MatcherXorNode): OredicNode { + const occurences = new Map(); + let newChildren: string[] = []; + + for (const child of node.children) { + if (child.type !== "oredic") { + this.setError( + 500, + "Unknown Error: Tried to apply xor on something else than an oredic node", + "applyExclusiveDisjunction", + ); + return this.newOredicNode(null); + } + + if (!child.children || child.children.length === 0) { + continue; + } + + const multiplier = child.negation ? -1 : 1; + + for (const oredic of child.children) { + const currentCount = occurences.get(oredic) ?? 0; + occurences.set(oredic, currentCount + multiplier); + } + } + + occurences.forEach((value, key) => { + if (Math.abs(value) % 2 === 1) { + newChildren.push(key); + } + }); + + if (node.negation) { + const dump = DUMPS.get(this.pack); + if (!dump) { + this.setError(500, "Failed to get dump for negated XOR operation"); + return this.newOredicNode(null); + } + + const allOredics = dump.split("\n").filter((line) => line.trim() !== ""); + const resultSet = new Set(newChildren); + newChildren = allOredics.filter((oredic) => !resultSet.has(oredic)); + } + + return this.newOredicNode(newChildren); } - occurences.forEach((value, key) => { - if (Math.abs(value) % 2 === 1) { - newChildren.push(key); - } - }); - - if (node.negation) { - const dump = DUMPS.get(this.pack); - if (!dump) { - this.setError(500, "Failed to get dump for negated XOR operation"); - return this.newOredicNode(null); - } - - const allOredics = dump.split("\n").filter((line) => line.trim() !== ""); - const resultSet = new Set(newChildren); - newChildren = allOredics.filter((oredic) => !resultSet.has(oredic)); + private buildRegexFromPattern(pattern: string): RegExp { + let regexPattern = "^"; + for (let i = 0; i < pattern.length; i++) { + if (pattern[i] === "*") { + regexPattern += ".+"; + } else { + regexPattern += pattern[i]; + } + } + regexPattern += "$"; + return new RegExp(regexPattern); } - return this.newOredicNode(newChildren); - } - - private buildRegexFromPattern(pattern: string): RegExp { - let regexPattern = "^"; - for (let i = 0; i < pattern.length; i++) { - if (pattern[i] === "*") { - regexPattern += ".+"; - } else { - regexPattern += pattern[i]; - } + private newOredicNode(children: string[] | null): OredicNode { + return { + type: NodeNames.OREDIC, + negation: false, + children: children ? children : [], + }; } - regexPattern += "$"; - return new RegExp(regexPattern); - } - - private newOredicNode(children: string[] | null): OredicNode { - return { - type: NodeNames.OREDIC, - negation: false, - children: children ? children : [], - }; - } } diff --git a/src/utils/parsers/OredicParser.ts b/src/utils/parsers/OredicParser.ts index c547e16..a9113ac 100644 --- a/src/utils/parsers/OredicParser.ts +++ b/src/utils/parsers/OredicParser.ts @@ -3,581 +3,537 @@ * https://github.com/AE2-UEL/Applied-Energistics-2/blob/26bb5986c636e9bdde62559b0d1c2bbc48c4b9e3/src/main/java/appeng/util/item/OreDictFilterMatcher.java#L11 */ -import { OredicPack } from "../../config/routes"; -import { StandardError } from "../../types/errors"; -import { - AstNode, - PatternNode, - OperatorNode, - Token, - OperatorChar, - SpecialChar, - NodeNames, - LexemeElement, - LexemeNames, - LexicalParseResult, -} from "../../types/parsing"; +import type { OredicPack } from "../../config/routes"; +import type { StandardError } from "../../types/errors"; +import type { AstNode, LexemeElement, OperatorNode, PatternNode, Token } from "../../types/parsing"; +import { LexemeNames, NodeNames, OperatorChar, SpecialChar } from "../../types/parsing"; import { ErrorProne } from "../parentClasses/ErrorProne"; type ParseState = { - ast: AstNode | null; - tokenBuffer: Token[]; - operandBuffer: AstNode[]; - currentOperator: "AND" | "XOR" | "OR" | null; - negationFlag: boolean; + ast: AstNode | null; + tokenBuffer: Token[]; + operandBuffer: AstNode[]; + currentOperator: "AND" | "XOR" | "OR" | null; + negationFlag: boolean; }; export class OredicParser extends ErrorProne { - private lexemeBuffer: string; - private parenthesesCount: number; - private optimizationPipeline: Array<(node: AstNode) => void>; + private lexemeBuffer: string; + private parenthesesCount: number; + private optimizationPipeline: Array<(node: AstNode) => void>; + + constructor(private pack: OredicPack) { + super("OredicParser"); + this.lexemeBuffer = ""; + this.parenthesesCount = 0; + + this.optimizationPipeline = [ + this.applyAssociativity.bind(this), + this.applyAnnihilatorXor.bind(this), + this.applyDeMorgan.bind(this), + this.applyWildcardConjunction.bind(this), + ]; + } + + parse(input: string): AstNode | StandardError { + this.resetState(); + + const { lexemeList } = this.lexicalParse(input.split("")); + + if (this.parenthesesCount !== 0) { + return this.setError(400, "Unbalanced parentheses", "parse"); + } - constructor(private pack: OredicPack) { - super("OredicParser"); - this.lexemeBuffer = ""; - this.parenthesesCount = 0; + const ast = this.parseNode(lexemeList); - this.optimizationPipeline = [ - this.applyAssociativity.bind(this), - this.applyAnnihilatorXor.bind(this), - this.applyDeMorgan.bind(this), - this.applyWildcardConjunction.bind(this), - ]; - } + if (this.isError(ast)) { + return this.propagateError(ast, "Failed to parse node", "parse"); + } - parse(input: string): AstNode | StandardError { - this.resetState(); + if (!ast) { + return this.setError(400, "Failed to parse input", "parse"); + } - const { lexemeList } = this.lexicalParse(input.split("")); + if (this.error.status) { + return this.error; + } - if (this.parenthesesCount !== 0) { - return this.setError(400, "Unbalanced parentheses", "parse"); + this.flattenAst(ast); + + if (this.error.status) { + return this.error; + } + + return ast; } - const ast = this.parseNode(lexemeList); + /* + * State management + */ - if (this.isError(ast)) { - return this.propagateError(ast, "Failed to parse node", "parse"); + private resetState(): void { + this.lexemeBuffer = ""; + this.parenthesesCount = 0; + this.error.status = false; + this.error.code = null; + this.error.message = null; } - if (!ast) { - return this.setError(400, "Failed to parse input", "parse"); + /* + * Lexical parsing + */ + + private lexicalParse(pattern: string[]): { + lexemeList: LexemeElement[]; + consumed: number; + } { + let lexemeList: LexemeElement[] = []; + + for (let i = 0; i < pattern.length; i++) { + switch (pattern[i]) { + case SpecialChar.SPACE: + break; + + case SpecialChar.GROUP_START: + lexemeList = this.flushLexemeBuffer(lexemeList); + this.incrementParentheses(); + + const result = this.lexicalParse(pattern.slice(i + 1)); + lexemeList.push({ + type: LexemeNames.GROUP, + content: result.lexemeList, + }); + + i += result.consumed; + + break; + case SpecialChar.GROUP_END: + lexemeList = this.flushLexemeBuffer(lexemeList); + this.decrementParentheses(); + return { lexemeList, consumed: i + 1 }; + + case OperatorChar.AND: + lexemeList = this.flushLexemeBuffer(lexemeList); + lexemeList.push({ type: LexemeNames.OPERATOR, content: "AND" }); + break; + case OperatorChar.XOR: + lexemeList = this.flushLexemeBuffer(lexemeList); + lexemeList.push({ type: LexemeNames.OPERATOR, content: "XOR" }); + break; + case OperatorChar.OR: + lexemeList = this.flushLexemeBuffer(lexemeList); + lexemeList.push({ type: LexemeNames.OPERATOR, content: "OR" }); + break; + + case SpecialChar.NEGATION: + lexemeList = this.flushLexemeBuffer(lexemeList); + lexemeList.push({ type: LexemeNames.NEGATION, content: null }); + break; + + case SpecialChar.WILDCARD: + lexemeList = this.flushLexemeBuffer(lexemeList); + lexemeList.push({ type: LexemeNames.WILDCARD, content: null }); + break; + + default: + this.lexemeBuffer += pattern[i]; + break; + } + } + + this.flushLexemeBuffer(lexemeList); + return { lexemeList, consumed: pattern.length }; } - if (this.error.status) { - return this.error; + private parseNode(lexemeList: LexemeElement[]): AstNode | null { + if (this.error.status) { + return null; + } + + const state: ParseState = { + ast: null, + tokenBuffer: [], + operandBuffer: [], + currentOperator: null, + negationFlag: false, + }; + + for (let i = 0; i < lexemeList.length; i++) { + switch (lexemeList[i].type) { + case "group": + const subLexeme = lexemeList[i].content; + if (typeof subLexeme === "string" || !subLexeme) { + this.setError(400, "Invalid group content", "parseNode"); + return null; + } + const subAst: AstNode | null = this.parseNode(subLexeme); + + if (this.error.status) { + return null; + } + + if (!subAst) { + this.setError(400, "Failed to parse group", "parseNode"); + return null; + } + + if (state.negationFlag) { + subAst.negation = true; + state.negationFlag = false; + } + + if (!state.ast) { + state.ast = subAst; + break; + } + + state.operandBuffer.push(subAst); + break; + + case "operator": + const operator = lexemeList[i].content; + if (operator !== "AND" && operator !== "OR" && operator !== "XOR") { + this.setError(400, "Invalid operator", "parseNode"); + return null; + } + + this.flushTokens(state); + + if (state.operandBuffer.length > 0 && state.currentOperator) { + this.flushOperands(state, state.currentOperator); + } + + state.currentOperator = operator; + break; + + case "negation": + if (state.tokenBuffer.length !== 0) { + this.setError(400, "Negation must come before pattern tokens", "parseNode"); + return null; + } + state.negationFlag = !state.negationFlag; + break; + + case "wildcard": + state.tokenBuffer.push({ type: NodeNames.WILDCARD }); + break; + + case "text": + const content = lexemeList[i].content; + if (typeof content !== "string") { + this.setError(400, "Invalid text content", "parseNode"); + return null; + } + state.tokenBuffer.push({ type: NodeNames.TEXT, content: content }); + break; + } + } + + return this.flushEnd(state); } - this.flattenAst(ast); + private flattenAst(ast: AstNode): void { + if (!ast) { + return; + } + + const currentAst = ast; + let previousAst: AstNode | null = null; + + while (JSON.stringify(currentAst) !== JSON.stringify(previousAst)) { + previousAst = JSON.parse(JSON.stringify(currentAst)); + + for (const fn of this.optimizationPipeline) { + fn(currentAst); + } + + if (currentAst.type === NodeNames.PATTERN) return; + + for (const child of currentAst.children) { + this.flattenAst(child); + } - if (this.error.status) { - return this.error; + if (this.error.status) return; + } } - return ast; - } + /* + * Buffer management + */ - /* - * State management - */ + private flushLexemeBuffer(lexemeList: LexemeElement[]): LexemeElement[] { + if (this.lexemeBuffer === "") { + return lexemeList; + } + lexemeList.push({ type: LexemeNames.TEXT, content: this.lexemeBuffer }); + this.lexemeBuffer = ""; + return lexemeList; + } - private resetState(): void { - this.lexemeBuffer = ""; - this.parenthesesCount = 0; - this.error.status = false; - this.error.code = null; - this.error.message = null; - } + private flushTokens(state: ParseState): void { + if (state.tokenBuffer.length > 0) { + state.operandBuffer.push(this.createPatternNode(state.tokenBuffer, state.negationFlag)); + state.tokenBuffer = []; + state.negationFlag = false; + } + } - /* - * Lexical parsing - */ + private flushOperands(state: ParseState, operator: "AND" | "XOR" | "OR"): void { + if (state.operandBuffer.length === 0) { + return; + } - private lexicalParse(pattern: string[]): { - lexemeList: LexemeElement[]; - consumed: number; - } { - let lexemeList: LexemeElement[] = []; + if (!state.ast) { + if (state.operandBuffer.length === 1) { + state.ast = state.operandBuffer[0]; + state.operandBuffer = []; + return; + } + + state.ast = { + type: NodeNames.OPERATOR, + operator: operator, + negation: false, + children: state.operandBuffer, + }; + state.operandBuffer = []; + return; + } - for (let i = 0; i < pattern.length; i++) { - switch (pattern[i]) { - case SpecialChar.SPACE: - break; + if (state.ast.type === NodeNames.OPERATOR && state.ast.operator === operator) { + state.ast.children = state.ast.children.concat(state.operandBuffer); + state.operandBuffer = []; + return; + } - case SpecialChar.GROUP_START: - lexemeList = this.flushLexemeBuffer(lexemeList); - this.incrementParentheses(); + state.ast = { + type: NodeNames.OPERATOR, + operator: operator, + negation: false, + children: [state.ast].concat(state.operandBuffer), + }; + state.operandBuffer = []; + } - const result = this.lexicalParse(pattern.slice(i + 1)); - lexemeList.push({ - type: LexemeNames.GROUP, - content: result.lexemeList, - }); + private flushEnd(state: ParseState): AstNode | null { + if (state.tokenBuffer.length > 0) { + const pattern = this.createPatternNode(state.tokenBuffer, state.negationFlag); - i += result.consumed; + if (!state.ast && !state.currentOperator) { + return pattern; + } - break; - case SpecialChar.GROUP_END: - lexemeList = this.flushLexemeBuffer(lexemeList); - this.decrementParentheses(); - return { lexemeList, consumed: i + 1 }; + state.operandBuffer.push(pattern); + } - case OperatorChar.AND: - lexemeList = this.flushLexemeBuffer(lexemeList); - lexemeList.push({ type: LexemeNames.OPERATOR, content: "AND" }); - break; - case OperatorChar.XOR: - lexemeList = this.flushLexemeBuffer(lexemeList); - lexemeList.push({ type: LexemeNames.OPERATOR, content: "XOR" }); - break; - case OperatorChar.OR: - lexemeList = this.flushLexemeBuffer(lexemeList); - lexemeList.push({ type: LexemeNames.OPERATOR, content: "OR" }); - break; + if (state.operandBuffer.length > 0 && state.currentOperator) { + this.flushOperands(state, state.currentOperator); + } - case SpecialChar.NEGATION: - lexemeList = this.flushLexemeBuffer(lexemeList); - lexemeList.push({ type: LexemeNames.NEGATION, content: null }); - break; + return state.ast; + } - case SpecialChar.WILDCARD: - lexemeList = this.flushLexemeBuffer(lexemeList); - lexemeList.push({ type: LexemeNames.WILDCARD, content: null }); - break; + /* + * Character classification and operator helpers + */ - default: - this.lexemeBuffer += pattern[i]; - break; - } + private isOperatorChar(char: string): boolean { + return char === OperatorChar.AND || char === OperatorChar.OR || char === OperatorChar.XOR; } - this.flushLexemeBuffer(lexemeList); - return { lexemeList, consumed: pattern.length }; - } - - private parseNode(lexemeList: LexemeElement[]): AstNode | null { - if (this.error.status) { - return null; - } - - const state: ParseState = { - ast: null, - tokenBuffer: [], - operandBuffer: [], - currentOperator: null, - negationFlag: false, - }; - - for (let i = 0; i < lexemeList.length; i++) { - switch (lexemeList[i].type) { - case "group": - const subLexeme = lexemeList[i].content; - if (typeof subLexeme === "string" || !subLexeme) { - this.setError(400, "Invalid group content", "parseNode"); - return null; - } - const subAst: AstNode | null = this.parseNode(subLexeme); + private getOperatorName(char: string): "AND" | "OR" | "XOR" { + switch (char) { + case OperatorChar.AND: + return "AND"; + case OperatorChar.OR: + return "OR"; + case OperatorChar.XOR: + return "XOR"; + default: + throw new Error(`Invalid operator character: ${char}`); + } + } - if (this.error.status) { - return null; - } + private incrementParentheses(): void { + this.parenthesesCount += 1; + } - if (!subAst) { - this.setError(400, "Failed to parse group", "parseNode"); - return null; - } + private decrementParentheses(): void { + if (this.parenthesesCount === 0) { + this.setError(400, "Closing parenthesis without opening", "decrementParentheses"); + return; + } + this.parenthesesCount -= 1; + } - if (state.negationFlag) { - subAst.negation = true; - state.negationFlag = false; - } + private createPatternNode(children: Token[], negation: boolean = false): PatternNode { + const node: PatternNode = { + type: NodeNames.PATTERN, + negation: negation, + children: children, + }; + return node; + } - if (!state.ast) { - state.ast = subAst; - break; - } + /* + * Optimization methods + */ + + // Associativity (OR, AND, XOR): OR: (a, OR: (b, c)) = OR: (a, b, c) + private applyAssociativity(node: AstNode): void { + if (!this.operatorAccepted(node, "all")) return; + const operatorNode = node as OperatorNode; + + const topLevelOperator = operatorNode.operator; + const newChildren: AstNode[] = []; + + for (let i = 0; i < operatorNode.children.length; i++) { + const child = operatorNode.children[i]; + if ( + child.type === NodeNames.OPERATOR && + child.operator === topLevelOperator && + child.negation === false + ) { + for (const subChild of child.children) { + newChildren.push(subChild); + } + } else { + newChildren.push(child); + } + } - state.operandBuffer.push(subAst); - break; + node.children = newChildren; + } - case "operator": - const operator = lexemeList[i].content; - if (operator !== "AND" && operator !== "OR" && operator !== "XOR") { - this.setError(400, "Invalid operator", "parseNode"); - return null; - } + // Distributivity of AND over OR: OR: (AND: (a, b), AND: (a, c)) = AND: (a, OR: (b, c)) + // Distributivity of OR over AND: AND: (OR: (a, b), OR: (a, c)) = OR: (a, AND: (b, c)) + private applyDistributivity(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND", "OR"])) return; + const operatorNode = node as OperatorNode; + } - this.flushTokens(state); + // Identity for OR: OR: (a, false) = a + private applyIdentityOr(node: AstNode): void { + if (!this.operatorAccepted(node, ["OR"])) return; + const operatorNode = node as OperatorNode; + } - if (state.operandBuffer.length > 0 && state.currentOperator) { - this.flushOperands(state, state.currentOperator); - } - - state.currentOperator = operator; - break; - - case "negation": - if (state.tokenBuffer.length !== 0) { - this.setError( - 400, - "Negation must come before pattern tokens", - "parseNode" - ); - return null; - } - state.negationFlag = !state.negationFlag; - break; - - case "wildcard": - state.tokenBuffer.push({ type: NodeNames.WILDCARD }); - break; - - case "text": - const content = lexemeList[i].content; - if (typeof content !== "string") { - this.setError(400, "Invalid text content", "parseNode"); - return null; - } - state.tokenBuffer.push({ type: NodeNames.TEXT, content: content }); - break; - } + // Identity for AND: AND: (a, true) = a + private applyIdentityAnd(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND"])) return; + const operatorNode = node as OperatorNode; } - return this.flushEnd(state); - } + // Identity for XOR: XOR: (a, false) = a + private applyIdentityXor(node: AstNode): void { + if (!this.operatorAccepted(node, ["XOR"])) return; + const operatorNode = node as OperatorNode; + } - private flattenAst(ast: AstNode): void { - if (!ast) { - return; + // Annihilator for OR: OR: (a, true) = true + private applyAnnihilatorOr(node: AstNode): void { + if (!this.operatorAccepted(node, ["OR"])) return; + const operatorNode = node as OperatorNode; } - let currentAst = ast; - let previousAst: AstNode | null = null; + // Annihilator for AND: AND: (a, false) = false => error + private applyAnnihilatorAnd(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND"])) return; + const operatorNode = node as OperatorNode; + } - while (JSON.stringify(currentAst) !== JSON.stringify(previousAst)) { - previousAst = JSON.parse(JSON.stringify(currentAst)); + // Annihilator for XOR: XOR: (a, a) = false => error + private applyAnnihilatorXor(node: AstNode): void { + if (!this.operatorAccepted(node, ["XOR"])) return; + const operatorNode = node as OperatorNode; - for (const fn of this.optimizationPipeline) { - fn(currentAst); - } + const uniqueChildren = new Map(); - if (currentAst.type === NodeNames.PATTERN) return; + for (const child of operatorNode.children) { + const hash = JSON.stringify(child); + if (uniqueChildren.has(hash)) { + this.setWarn("Invalid Logic: XOR has two identical elements. XOR(a,a) is always false"); + return; + } + uniqueChildren.set(hash, true); + } + } - for (const child of currentAst.children) { - this.flattenAst(child); - } + // Idempotence (OR, AND): OR: (a, a) = a + private applyIdempotence(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND", "OR"])) return; + const operatorNode = node as OperatorNode; + } - if (this.error.status) return; + // Absorption (OR, AND): AND: (a, OR: (a, b)) = a + private applyAbsorption(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND", "OR"])) return; + const operatorNode = node as OperatorNode; } - } - /* - * Buffer management - */ + // Complementation for AND: AND: (a, NOT(a)) = false => error + private applyComplementationAnd(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND"])) return; + const operatorNode = node as OperatorNode; + } - private flushLexemeBuffer(lexemeList: LexemeElement[]): LexemeElement[] { - if (this.lexemeBuffer === "") { - return lexemeList; + // Complementation for OR: OR: (a, NOT(a)) = true + private applyComplementationOr(node: AstNode): void { + if (!this.operatorAccepted(node, ["OR"])) return; + const operatorNode = node as OperatorNode; } - lexemeList.push({ type: LexemeNames.TEXT, content: this.lexemeBuffer }); - this.lexemeBuffer = ""; - return lexemeList; - } - private flushTokens(state: ParseState): void { - if (state.tokenBuffer.length > 0) { - state.operandBuffer.push( - this.createPatternNode(state.tokenBuffer, state.negationFlag) - ); - state.tokenBuffer = []; - state.negationFlag = false; + // Complementary for XOR: XOR(a, true) = NOT(a), XOR(a, NOT(a)) = true + // Complementation for OR: OR: (a, NOT(a)) = true + private applyComplementaryOrs(node: AstNode): void { + if (!this.operatorAccepted(node, ["XOR"])) return; + const operatorNode = node as OperatorNode; } - } - private flushOperands( - state: ParseState, - operator: "AND" | "XOR" | "OR" - ): void { - if (state.operandBuffer.length === 0) { - return; + // Double negation: NOT(NOT(a)) = a + private applyDoubleNegation(node: AstNode): void { + if (!this.operatorAccepted(node, "all")) return; + const operatorNode = node as OperatorNode; } - if (!state.ast) { - if (state.operandBuffer.length === 1) { - state.ast = state.operandBuffer[0]; - state.operandBuffer = []; - return; - } - - state.ast = { - type: NodeNames.OPERATOR, - operator: operator, - negation: false, - children: state.operandBuffer, - }; - state.operandBuffer = []; - return; - } - - if ( - state.ast.type === NodeNames.OPERATOR && - state.ast.operator === operator - ) { - state.ast.children = state.ast.children.concat(state.operandBuffer); - state.operandBuffer = []; - return; - } - - state.ast = { - type: NodeNames.OPERATOR, - operator: operator, - negation: false, - children: [state.ast].concat(state.operandBuffer), - }; - state.operandBuffer = []; - } - - private flushEnd(state: ParseState): AstNode | null { - if (state.tokenBuffer.length > 0) { - const pattern = this.createPatternNode( - state.tokenBuffer, - state.negationFlag - ); - - if (!state.ast && !state.currentOperator) { - return pattern; - } - - state.operandBuffer.push(pattern); - } - - if (state.operandBuffer.length > 0 && state.currentOperator) { - this.flushOperands(state, state.currentOperator); - } - - return state.ast; - } - - /* - * Character classification and operator helpers - */ - - private isOperatorChar(char: string): boolean { - return ( - char === OperatorChar.AND || - char === OperatorChar.OR || - char === OperatorChar.XOR - ); - } - - private getOperatorName(char: string): "AND" | "OR" | "XOR" { - switch (char) { - case OperatorChar.AND: - return "AND"; - case OperatorChar.OR: - return "OR"; - case OperatorChar.XOR: - return "XOR"; - default: - throw new Error(`Invalid operator character: ${char}`); - } - } - - private incrementParentheses(): void { - this.parenthesesCount += 1; - } - - private decrementParentheses(): void { - if (this.parenthesesCount === 0) { - this.setError( - 400, - "Closing parenthesis without opening", - "decrementParentheses" - ); - return; - } - this.parenthesesCount -= 1; - } - - private createPatternNode( - children: Token[], - negation: boolean = false - ): PatternNode { - const node: PatternNode = { - type: NodeNames.PATTERN, - negation: negation, - children: children, - }; - return node; - } - - /* - * Optimization methods - */ - - // Associativity (OR, AND, XOR): OR: (a, OR: (b, c)) = OR: (a, b, c) - private applyAssociativity(node: AstNode): void { - if (!this.operatorAccepted(node, "all")) return; - const operatorNode = node as OperatorNode; - - const topLevelOperator = operatorNode.operator; - const newChildren: AstNode[] = []; - - for (let i = 0; i < operatorNode.children.length; i++) { - const child = operatorNode.children[i]; - if ( - child.type === NodeNames.OPERATOR && - child.operator === topLevelOperator && - child.negation === false - ) { - for (const subChild of child.children) { - newChildren.push(subChild); + // De Morgan's laws: OR: (NOT(a), NOT(b)) = NOT(AND: (a, b)) + private applyDeMorgan(node: AstNode): void { + if (!this.operatorAccepted(node, ["AND", "OR"])) return; + const operatorNode = node as OperatorNode; + + let allNegation = true; + + for (const child of operatorNode.children) { + if (!child.negation) allNegation = false; } - } else { - newChildren.push(child); - } - } - - node.children = newChildren; - } - - // Distributivity of AND over OR: OR: (AND: (a, b), AND: (a, c)) = AND: (a, OR: (b, c)) - // Distributivity of OR over AND: AND: (OR: (a, b), OR: (a, c)) = OR: (a, AND: (b, c)) - private applyDistributivity(node: AstNode): void { - if (!this.operatorAccepted(node, ["AND", "OR"])) return; - const operatorNode = node as OperatorNode; - } - - // Identity for OR: OR: (a, false) = a - private applyIdentityOr(node: AstNode): void { - if (!this.operatorAccepted(node, ["OR"])) return; - const operatorNode = node as OperatorNode; - } - - // Identity for AND: AND: (a, true) = a - private applyIdentityAnd(node: AstNode): void { - if (!this.operatorAccepted(node, ["AND"])) return; - const operatorNode = node as OperatorNode; - } - - // Identity for XOR: XOR: (a, false) = a - private applyIdentityXor(node: AstNode): void { - if (!this.operatorAccepted(node, ["XOR"])) return; - const operatorNode = node as OperatorNode; - } - - // Annihilator for OR: OR: (a, true) = true - private applyAnnihilatorOr(node: AstNode): void { - if (!this.operatorAccepted(node, ["OR"])) return; - const operatorNode = node as OperatorNode; - } - - // Annihilator for AND: AND: (a, false) = false => error - private applyAnnihilatorAnd(node: AstNode): void { - if (!this.operatorAccepted(node, ["AND"])) return; - const operatorNode = node as OperatorNode; - } - - // Annihilator for XOR: XOR: (a, a) = false => error - private applyAnnihilatorXor(node: AstNode): void { - if (!this.operatorAccepted(node, ["XOR"])) return; - const operatorNode = node as OperatorNode; - - const uniqueChildren = new Map(); - - for (const child of operatorNode.children) { - const hash = JSON.stringify(child); - if (uniqueChildren.has(hash)) { - this.setWarn( - "Invalid Logic: XOR has two identical elements. XOR(a,a) is always false" - ); - return; - } - uniqueChildren.set(hash, true); - } - } - - // Idempotence (OR, AND): OR: (a, a) = a - private applyIdempotence(node: AstNode): void { - if (!this.operatorAccepted(node, ["AND", "OR"])) return; - const operatorNode = node as OperatorNode; - } - - // Absorption (OR, AND): AND: (a, OR: (a, b)) = a - private applyAbsorption(node: AstNode): void { - if (!this.operatorAccepted(node, ["AND", "OR"])) return; - const operatorNode = node as OperatorNode; - } - - // Complementation for AND: AND: (a, NOT(a)) = false => error - private applyComplementationAnd(node: AstNode): void { - if (!this.operatorAccepted(node, ["AND"])) return; - const operatorNode = node as OperatorNode; - } - - // Complementation for OR: OR: (a, NOT(a)) = true - private applyComplementationOr(node: AstNode): void { - if (!this.operatorAccepted(node, ["OR"])) return; - const operatorNode = node as OperatorNode; - } - - // Complementary for XOR: XOR(a, true) = NOT(a), XOR(a, NOT(a)) = true - // Complementation for OR: OR: (a, NOT(a)) = true - private applyComplementaryOrs(node: AstNode): void { - if (!this.operatorAccepted(node, ["XOR"])) return; - const operatorNode = node as OperatorNode; - } - - // Double negation: NOT(NOT(a)) = a - private applyDoubleNegation(node: AstNode): void { - if (!this.operatorAccepted(node, "all")) return; - const operatorNode = node as OperatorNode; - } - - // De Morgan's laws: OR: (NOT(a), NOT(b)) = NOT(AND: (a, b)) - private applyDeMorgan(node: AstNode): void { - if (!this.operatorAccepted(node, ["AND", "OR"])) return; - const operatorNode = node as OperatorNode; - - let allNegation = true; - - for (const child of operatorNode.children) { - if (!child.negation) allNegation = false; - } - - if (allNegation) { - const complemetary = operatorNode.operator === "OR" ? "AND" : "OR"; - operatorNode.negation = true; - operatorNode.operator = complemetary; - operatorNode.children.forEach((child) => { - child.negation = false; - }); - } - - node = operatorNode; - } - - private applyWildcardConjunction(node: AstNode): void { - if (node.type !== NodeNames.PATTERN) return; - for (let i = 1; i < node.children.length; i++) { - const lastChild = node.children[i - 1]; - const currChild = node.children[i]; - if ( - currChild.type === NodeNames.WILDCARD && - lastChild.type === NodeNames.WILDCARD - ) { - node.children.splice(i, 1); - i -= 1; - } - } - } - - private operatorAccepted( - node: AstNode, - acceptedOperators: Array<"AND" | "OR" | "XOR"> | "all" - ): boolean { - if (node.type === NodeNames.PATTERN) return false; - if (acceptedOperators === "all") return true; - return acceptedOperators.includes(node.operator); - } + + if (allNegation) { + const complemetary = operatorNode.operator === "OR" ? "AND" : "OR"; + operatorNode.negation = true; + operatorNode.operator = complemetary; + operatorNode.children.forEach((child) => { + child.negation = false; + }); + } + + node = operatorNode; + } + + private applyWildcardConjunction(node: AstNode): void { + if (node.type !== NodeNames.PATTERN) return; + for (let i = 1; i < node.children.length; i++) { + const lastChild = node.children[i - 1]; + const currChild = node.children[i]; + if (currChild.type === NodeNames.WILDCARD && lastChild.type === NodeNames.WILDCARD) { + node.children.splice(i, 1); + i -= 1; + } + } + } + + private operatorAccepted(node: AstNode, acceptedOperators: Array<"AND" | "OR" | "XOR"> | "all"): boolean { + if (node.type === NodeNames.PATTERN) return false; + if (acceptedOperators === "all") return true; + return acceptedOperators.includes(node.operator); + } } diff --git a/src/utils/parsers/OredicShortener.ts b/src/utils/parsers/OredicShortener.ts index a01af5f..1591cb8 100644 --- a/src/utils/parsers/OredicShortener.ts +++ b/src/utils/parsers/OredicShortener.ts @@ -1,186 +1,179 @@ +import type { OredicPack } from "../../config/routes"; +import { getLogger } from "../../helpers/Logger"; +import { startTimer, stopTimer, Timer } from "../../helpers/Timer"; import { DUMPS } from "../../loaders/storage"; -import { AstNode, NodeNames } from "../../types/parsing"; -import { StandardError } from "../../types/errors"; -import { TimerRes } from "../../types/timer"; -import { getLogger } from "../Logger"; +import type { StandardError } from "../../types/errors"; +import type { TimerRes } from "../../types/timer"; import { ErrorProne } from "../parentClasses/ErrorProne"; import { OredicMatcher } from "./OredicMatcher"; -import { startTimer, stopTimer, queryTimer, Timer } from "../Timer"; -import { OredicPack } from "../../config/routes"; const PROGRESS_UPDATE_INTERVAL = 10; type TelemetryResults = { - result: { - string: string; - length: number; - shortening: number; - }; - timings: TimerRes; + result: { + string: string; + length: number; + shortening: number; + }; + timings: TimerRes; }; export class OredicShortener extends ErrorProne { - private dump: string[]; - private matcher: OredicMatcher; - private telemetry: Map; - private shortcuts: Map; - - constructor(private pack: OredicPack) { - super("OredicShortener"); - this.dump = this.loadDump(); - this.matcher = new OredicMatcher(this.pack); - this.telemetry = new Map(); - this.shortcuts = new Map(); - } - - getShortcuts(): Map | StandardError { - //TODO: Implement caching strategy!!! - - if (this.shortcuts.size === 0) { - const result = this.findAll(); - if (this.isError(result)) { - return this.propagateError(result, "Failed to generate shortcuts", "getShortcuts"); - } - this.shortcuts = result; + private dump: string[]; + private matcher: OredicMatcher; + private telemetry: Map; + private shortcuts: Map; + + constructor(private pack: OredicPack) { + super("OredicShortener"); + this.dump = this.loadDump(); + this.matcher = new OredicMatcher(this.pack); + this.telemetry = new Map(); + this.shortcuts = new Map(); } - return this.shortcuts; - } - - getTelemetry(): Map { - return new Map(this.telemetry); - } - - private findAll(): Map { - getLogger().simpleLog("info", `Finding shortest patterns for ${this.pack}`); - startTimer("shortening"); - - const result = new Map(); - - for (let i = 0; i < this.dump.length; i++) { - const oredicTimer = new Timer(); - const oredic = this.dump[i]; - const pattern = this.findShortest(oredic, i); - result.set(oredic, pattern); - - const timerResult = oredicTimer.getTime(); - this.telemetry.set(oredic, { - timings: timerResult, - result: { - string: pattern, - length: pattern.length, - shortening: oredic.length - pattern.length, - }, - }); - - if ( - (i + 1) % PROGRESS_UPDATE_INTERVAL === 0 || - i === this.dump.length - 1 - ) { - this.logProgress(i + 1, this.dump.length); - } + initShortcuts() { + const shortcuts = this.getShortcuts(); + if (this.isError(shortcuts)) { + return this.propagateError(shortcuts, "Could not initialize shortcuts", "initShortcuts"); + } + if (this.shortcuts.size === 0) { + return this.setError(500, "Could not initialize shortcuts", "initShortcuts"); + } } - const time = stopTimer("shortening").getTime(); - getLogger().simpleLog( - "success", - `Found ${result.size} patterns in ${time.formatted}` - ); + getShortcuts(): Map | StandardError { + //TODO: Implement caching strategy!!! - return result; - } + if (this.shortcuts.size === 0) { + const result = this.findAll(); + if (this.isError(result)) { + return this.propagateError(result, "Failed to generate shortcuts", "getShortcuts"); + } + this.shortcuts = result; + } - private findShortest(oredic: string, oredicIndex: number): string { - const maxUsefulLength = oredic.length - 2; + return this.shortcuts; + } - for (let length = 1; length <= maxUsefulLength; length++) { - const pattern = this.tryLength(oredic, oredicIndex, length); - if (pattern) return pattern; + getTelemetry(): Map { + return new Map(this.telemetry); } - return oredic; - } - - private tryLength( - text: string, - oredicIndex: number, - length: number - ): string | null { - return this.tryPositions(text, oredicIndex, length, 0, []); - } - - private tryPositions( - text: string, - oredicIndex: number, - remaining: number, - start: number, - positions: number[] - ): string | null { - if (remaining === 0) { - if (this.hasUselessGaps(positions)) { - return null; - } + private findAll(): Map { + getLogger().simpleLog("info", `Finding shortest patterns for ${this.pack}`); + startTimer("shortening"); + + const result = new Map(); + + for (let i = 0; i < this.dump.length; i++) { + const oredicTimer = new Timer(); + const oredic = this.dump[i]; + const pattern = this.findShortest(oredic, i); + result.set(oredic, pattern); + + const timerResult = oredicTimer.getTime(); + this.telemetry.set(oredic, { + timings: timerResult, + result: { + string: pattern, + length: pattern.length, + shortening: oredic.length - pattern.length, + }, + }); + + if ((i + 1) % PROGRESS_UPDATE_INTERVAL === 0 || i === this.dump.length - 1) { + this.logProgress(i + 1, this.dump.length); + } + } + + const time = stopTimer("shortening").getTime(); + getLogger().simpleLog("success", `Found ${result.size} patterns in ${time.formatted}`); + + return result; + } - const pattern = this.buildPattern(text, positions); - return this.matcher.isUniqueMatch(pattern, oredicIndex) ? pattern : null; + private findShortest(oredic: string, oredicIndex: number): string { + const maxUsefulLength = oredic.length - 2; + + for (let length = 1; length <= maxUsefulLength; length++) { + const pattern = this.tryLength(oredic, oredicIndex, length); + if (pattern) return pattern; + } + + return oredic; } - const maxPos = text.length - remaining; - for (let pos = start; pos <= maxPos; pos++) { - positions.push(pos); - const result = this.tryPositions( - text, - oredicIndex, - remaining - 1, - pos + 1, - positions - ); - if (result) return result; - positions.pop(); + private tryLength(text: string, oredicIndex: number, length: number): string | null { + return this.tryPositions(text, oredicIndex, length, 0, []); } - return null; - } + private tryPositions( + text: string, + oredicIndex: number, + remaining: number, + start: number, + positions: number[], + ): string | null { + if (remaining === 0) { + if (this.hasUselessGaps(positions)) { + return null; + } + + const pattern = this.buildPattern(text, positions); + return this.matcher.isUniqueMatch(pattern, oredicIndex) ? pattern : null; + } + + const maxPos = text.length - remaining; + for (let pos = start; pos <= maxPos; pos++) { + positions.push(pos); + const result = this.tryPositions(text, oredicIndex, remaining - 1, pos + 1, positions); + if (result) return result; + positions.pop(); + } - private hasUselessGaps(positions: number[]): boolean { - for (let i = 0; i < positions.length - 1; i++) { - const gapSize = positions[i + 1] - positions[i] - 1; - if (gapSize === 1) { - return true; - } + return null; } - return false; - } - private buildPattern(text: string, positions: number[]): string { - if (positions.length === 0) return ""; + private hasUselessGaps(positions: number[]): boolean { + for (let i = 0; i < positions.length - 1; i++) { + const gapSize = positions[i + 1] - positions[i] - 1; + if (gapSize === 1) { + return true; + } + } + return false; + } - const parts: string[] = []; + private buildPattern(text: string, positions: number[]): string { + if (positions.length === 0) return ""; - if (positions[0] > 0) parts.push("*"); + const parts: string[] = []; - for (let i = 0; i < positions.length; i++) { - parts.push(text[positions[i]]); + if (positions[0] > 0) parts.push("*"); - if (i < positions.length - 1 && positions[i + 1] > positions[i] + 1) { - parts.push("*"); - } - } + for (let i = 0; i < positions.length; i++) { + parts.push(text[positions[i]]); - if (positions[positions.length - 1] < text.length - 1) parts.push("*"); + if (i < positions.length - 1 && positions[i + 1] > positions[i] + 1) { + parts.push("*"); + } + } - return parts.join(""); - } + if (positions[positions.length - 1] < text.length - 1) parts.push("*"); - private loadDump(): string[] { - const dumpContent = DUMPS.get(this.pack); - if (!dumpContent) { - throw new Error(`Failed to load oredic dump for pack: ${this.pack}`); + return parts.join(""); } - return dumpContent.split("\n"); - } - private logProgress(current: number, total: number): void { - getLogger().progressBar(current, total, "shortening", 50, "Processing"); - } + private loadDump(): string[] { + const dumpContent = DUMPS.get(this.pack); + if (!dumpContent) { + throw new Error(`Failed to load oredic dump for pack: ${this.pack}`); + } + return dumpContent.split("\n"); + } + + private logProgress(current: number, total: number): void { + getLogger().progressBar(current, total, "shortening", 50, "Processing"); + } } diff --git a/storage/filters/drugs.json b/storage/filters/drugs.json index ab21f99..28578d3 100644 --- a/storage/filters/drugs.json +++ b/storage/filters/drugs.json @@ -1,42 +1,41 @@ { - "name": "Illegal Drugs", - "description": "Filters content related to drug synthesis, manufacturing, and trafficking", - "patterns": [ - { - "pattern": "\\b(synthesize|manufacture|make|produce|cook)\\s+(meth|methamphetamine|cocaine|heroin|fentanyl|lsd|mdma|ecstasy)\\b", - "severity": "high", - "description": "Drug synthesis or manufacturing" - }, - { - "pattern": "\\b(drug\\s+)?(synthesis|manufacturing|production)\\s+(guide|tutorial|method|process)\\b", - "severity": "high", - "description": "Drug production guides" - }, - { - "pattern": "\\bcrack\\s+cocaine\\s+(recipe|method|process)\\b", - "severity": "high", - "description": "Crack cocaine production" - }, - { - "pattern": "\\b(grow|cultivate)\\s+marijuana\\s+(indoor|commercial|large\\s*scale)\\b", - "severity": "medium", - "description": "Large-scale marijuana cultivation" - }, - { - "pattern": "\\b(extract|purify)\\s+(cocaine|heroin|fentanyl|opioid)\\b", - "severity": "high", - "description": "Drug extraction or purification" - }, - { - "pattern": "\\b(meth|methamphetamine)\\s+(lab|recipe|cook|production)\\b", - "severity": "high", - "description": "Methamphetamine production" - }, - { - "pattern": "\\b(drug\\s+)?(trafficking|smuggling)\\s+(method|route|strategy)\\b", - "severity": "high", - "description": "Drug trafficking methods" - } - ] + "name": "Illegal Drugs", + "description": "Filters content related to drug synthesis, manufacturing, and trafficking", + "patterns": [ + { + "pattern": "\\b(synthesize|manufacture|make|produce|cook)\\s+(meth|methamphetamine|cocaine|heroin|fentanyl|lsd|mdma|ecstasy)\\b", + "severity": "high", + "description": "Drug synthesis or manufacturing" + }, + { + "pattern": "\\b(drug\\s+)?(synthesis|manufacturing|production)\\s+(guide|tutorial|method|process)\\b", + "severity": "high", + "description": "Drug production guides" + }, + { + "pattern": "\\bcrack\\s+cocaine\\s+(recipe|method|process)\\b", + "severity": "high", + "description": "Crack cocaine production" + }, + { + "pattern": "\\b(grow|cultivate)\\s+marijuana\\s+(indoor|commercial|large\\s*scale)\\b", + "severity": "medium", + "description": "Large-scale marijuana cultivation" + }, + { + "pattern": "\\b(extract|purify)\\s+(cocaine|heroin|fentanyl|opioid)\\b", + "severity": "high", + "description": "Drug extraction or purification" + }, + { + "pattern": "\\b(meth|methamphetamine)\\s+(lab|recipe|cook|production)\\b", + "severity": "high", + "description": "Methamphetamine production" + }, + { + "pattern": "\\b(drug\\s+)?(trafficking|smuggling)\\s+(method|route|strategy)\\b", + "severity": "high", + "description": "Drug trafficking methods" + } + ] } - diff --git a/storage/filters/exploitation.json b/storage/filters/exploitation.json index 87efc86..67e376e 100644 --- a/storage/filters/exploitation.json +++ b/storage/filters/exploitation.json @@ -1,32 +1,31 @@ { - "name": "Exploitation", - "description": "Filters content related to child exploitation and human trafficking", - "patterns": [ - { - "pattern": "\\b(child|minor)\\s+(exploitation|abuse|pornography|grooming)\\b", - "severity": "critical", - "description": "Child exploitation or abuse" - }, - { - "pattern": "\\b(human|sex)\\s+trafficking\\s+(guide|method|how\\s+to)\\b", - "severity": "critical", - "description": "Human trafficking" - }, - { - "pattern": "\\b(groom|lure|manipulate)\\s+(child|minor|kid)\\b", - "severity": "critical", - "description": "Child grooming" - }, - { - "pattern": "\\b(sexual\\s+)?exploitation\\s+(of\\s+)?(minor|child)\\b", - "severity": "critical", - "description": "Exploitation of minors" - }, - { - "pattern": "\\b(child|minor)\\s+(sexual|pornographic)\\s+(content|material)\\b", - "severity": "critical", - "description": "Child sexual abuse material" - } - ] + "name": "Exploitation", + "description": "Filters content related to child exploitation and human trafficking", + "patterns": [ + { + "pattern": "\\b(child|minor)\\s+(exploitation|abuse|pornography|grooming)\\b", + "severity": "critical", + "description": "Child exploitation or abuse" + }, + { + "pattern": "\\b(human|sex)\\s+trafficking\\s+(guide|method|how\\s+to)\\b", + "severity": "critical", + "description": "Human trafficking" + }, + { + "pattern": "\\b(groom|lure|manipulate)\\s+(child|minor|kid)\\b", + "severity": "critical", + "description": "Child grooming" + }, + { + "pattern": "\\b(sexual\\s+)?exploitation\\s+(of\\s+)?(minor|child)\\b", + "severity": "critical", + "description": "Exploitation of minors" + }, + { + "pattern": "\\b(child|minor)\\s+(sexual|pornographic)\\s+(content|material)\\b", + "severity": "critical", + "description": "Child sexual abuse material" + } + ] } - diff --git a/storage/filters/exploits.json b/storage/filters/exploits.json index e1abdf1..3b3108e 100644 --- a/storage/filters/exploits.json +++ b/storage/filters/exploits.json @@ -1,21 +1,21 @@ { - "name": "Exploits", - "description": "Filters smart people trying to do smart things", - "patterns": [ - { - "pattern": "", - "severity": "high", - "description": "Trying to override the user" - }, - { - "pattern": "\\b(prompt|question):", - "severity": "medium", - "description": "Trying to override the prompt" - }, - { - "pattern": "\\bcontext:", - "severity": "medium", - "description": "Trying to override the context" - } - ] + "name": "Exploits", + "description": "Filters smart people trying to do smart things", + "patterns": [ + { + "pattern": "", + "severity": "high", + "description": "Trying to override the user" + }, + { + "pattern": "\\b(prompt|question):", + "severity": "medium", + "description": "Trying to override the prompt" + }, + { + "pattern": "\\bcontext:", + "severity": "medium", + "description": "Trying to override the context" + } + ] } diff --git a/storage/filters/fraud.json b/storage/filters/fraud.json index 22f4ae0..d7e520e 100644 --- a/storage/filters/fraud.json +++ b/storage/filters/fraud.json @@ -1,47 +1,46 @@ { - "name": "Fraud & Financial Crime", - "description": "Filters content related to fraud, money laundering, and financial crimes", - "patterns": [ - { - "pattern": "\\b(counterfeit|fake)\\s+(money|currency|bills|documents|id)\\b", - "severity": "high", - "description": "Counterfeiting" - }, - { - "pattern": "\\b(credit\\s*card|identity)\\s+(fraud|theft|cloning)\\b", - "severity": "high", - "description": "Credit card fraud or identity theft" - }, - { - "pattern": "\\b(money\\s*laundering|wash\\s+money)\\s+(method|scheme|process)\\b", - "severity": "high", - "description": "Money laundering" - }, - { - "pattern": "\\b(tax\\s+evasion|evade\\s+tax)\\s+(method|scheme|strategy)\\b", - "severity": "medium", - "description": "Tax evasion" - }, - { - "pattern": "\\b(ponzi|pyramid)\\s+scheme\\s+(start|create|run)\\b", - "severity": "high", - "description": "Ponzi or pyramid schemes" - }, - { - "pattern": "\\b(insurance|welfare)\\s+fraud\\s+(commit|scheme)\\b", - "severity": "high", - "description": "Insurance or welfare fraud" - }, - { - "pattern": "\\b(skimming\\s+device|card\\s+skimmer)\\s+(make|build|install)\\b", - "severity": "high", - "description": "Card skimming devices" - }, - { - "pattern": "\\b(fake\\s+id|forged\\s+document)\\s+(create|make|produce)\\b", - "severity": "high", - "description": "Forging documents" - } - ] + "name": "Fraud & Financial Crime", + "description": "Filters content related to fraud, money laundering, and financial crimes", + "patterns": [ + { + "pattern": "\\b(counterfeit|fake)\\s+(money|currency|bills|documents|id)\\b", + "severity": "high", + "description": "Counterfeiting" + }, + { + "pattern": "\\b(credit\\s*card|identity)\\s+(fraud|theft|cloning)\\b", + "severity": "high", + "description": "Credit card fraud or identity theft" + }, + { + "pattern": "\\b(money\\s*laundering|wash\\s+money)\\s+(method|scheme|process)\\b", + "severity": "high", + "description": "Money laundering" + }, + { + "pattern": "\\b(tax\\s+evasion|evade\\s+tax)\\s+(method|scheme|strategy)\\b", + "severity": "medium", + "description": "Tax evasion" + }, + { + "pattern": "\\b(ponzi|pyramid)\\s+scheme\\s+(start|create|run)\\b", + "severity": "high", + "description": "Ponzi or pyramid schemes" + }, + { + "pattern": "\\b(insurance|welfare)\\s+fraud\\s+(commit|scheme)\\b", + "severity": "high", + "description": "Insurance or welfare fraud" + }, + { + "pattern": "\\b(skimming\\s+device|card\\s+skimmer)\\s+(make|build|install)\\b", + "severity": "high", + "description": "Card skimming devices" + }, + { + "pattern": "\\b(fake\\s+id|forged\\s+document)\\s+(create|make|produce)\\b", + "severity": "high", + "description": "Forging documents" + } + ] } - diff --git a/storage/filters/hacking.json b/storage/filters/hacking.json index fab3e59..837e6d2 100644 --- a/storage/filters/hacking.json +++ b/storage/filters/hacking.json @@ -1,52 +1,51 @@ { - "name": "Hacking & Cybercrime", - "description": "Filters content related to malware, hacking, and cyber attacks", - "patterns": [ - { - "pattern": "\\b(create|make|build|develop)\\s+(malware|ransomware|virus|trojan|keylogger|botnet)\\b", - "severity": "high", - "description": "Creating malware" - }, - { - "pattern": "\\b(ddos|dos)\\s+(attack|tool|script|service)\\b", - "severity": "high", - "description": "DDoS attacks" - }, - { - "pattern": "\\b(steal|crack|bypass)\\s+(password|credential|login)\\b", - "severity": "high", - "description": "Credential theft" - }, - { - "pattern": "\\b(exploit|vulnerability)\\s+(kit|framework|tutorial)\\b", - "severity": "high", - "description": "Exploit kits or tutorials" - }, - { - "pattern": "\\bsql\\s*injection\\s+(tutorial|guide|attack)\\b", - "severity": "high", - "description": "SQL injection attacks" - }, - { - "pattern": "\\b(bypass|defeat|circumvent)\\s+(encryption|security|authentication)\\b", - "severity": "high", - "description": "Bypassing security" - }, - { - "pattern": "\\b(credit\\s*card|bank\\s*account)\\s+(hack|exploit|fraud)\\b", - "severity": "high", - "description": "Financial hacking" - }, - { - "pattern": "\\b(zero\\s*day|0day)\\s+(exploit|vulnerability)\\s+(sell|buy|use)\\b", - "severity": "high", - "description": "Zero-day exploits" - }, - { - "pattern": "\\b(phishing\\s+)?(kit|campaign|attack)\\s+(setup|create|launch)\\b", - "severity": "high", - "description": "Phishing campaigns" - } - ] + "name": "Hacking & Cybercrime", + "description": "Filters content related to malware, hacking, and cyber attacks", + "patterns": [ + { + "pattern": "\\b(create|make|build|develop)\\s+(malware|ransomware|virus|trojan|keylogger|botnet)\\b", + "severity": "high", + "description": "Creating malware" + }, + { + "pattern": "\\b(ddos|dos)\\s+(attack|tool|script|service)\\b", + "severity": "high", + "description": "DDoS attacks" + }, + { + "pattern": "\\b(steal|crack|bypass)\\s+(password|credential|login)\\b", + "severity": "high", + "description": "Credential theft" + }, + { + "pattern": "\\b(exploit|vulnerability)\\s+(kit|framework|tutorial)\\b", + "severity": "high", + "description": "Exploit kits or tutorials" + }, + { + "pattern": "\\bsql\\s*injection\\s+(tutorial|guide|attack)\\b", + "severity": "high", + "description": "SQL injection attacks" + }, + { + "pattern": "\\b(bypass|defeat|circumvent)\\s+(encryption|security|authentication)\\b", + "severity": "high", + "description": "Bypassing security" + }, + { + "pattern": "\\b(credit\\s*card|bank\\s*account)\\s+(hack|exploit|fraud)\\b", + "severity": "high", + "description": "Financial hacking" + }, + { + "pattern": "\\b(zero\\s*day|0day)\\s+(exploit|vulnerability)\\s+(sell|buy|use)\\b", + "severity": "high", + "description": "Zero-day exploits" + }, + { + "pattern": "\\b(phishing\\s+)?(kit|campaign|attack)\\s+(setup|create|launch)\\b", + "severity": "high", + "description": "Phishing campaigns" + } + ] } - diff --git a/storage/filters/poisons.json b/storage/filters/poisons.json index 0dbc11c..a119e74 100644 --- a/storage/filters/poisons.json +++ b/storage/filters/poisons.json @@ -1,32 +1,31 @@ { - "name": "Dangerous Substances & Poisons", - "description": "Filters content related to poisons, toxins, and chemical/biological weapons", - "patterns": [ - { - "pattern": "\\b(poison|toxin)\\s+(recipe|method|create|make)\\b", - "severity": "high", - "description": "Creating poisons or toxins" - }, - { - "pattern": "\\b(ricin|cyanide|arsenic)\\s+(extract|produce|obtain)\\b", - "severity": "critical", - "description": "Specific deadly poisons" - }, - { - "pattern": "\\b(deadly|lethal)\\s+(poison|substance|chemical)\\b", - "severity": "high", - "description": "Lethal substances" - }, - { - "pattern": "\\b(nerve\\s+agent|chemical\\s+weapon)\\s+(create|make|synthesize)\\b", - "severity": "critical", - "description": "Chemical weapons" - }, - { - "pattern": "\\b(biological\\s+weapon|bioweapon)\\s+(create|develop)\\b", - "severity": "critical", - "description": "Biological weapons" - } - ] + "name": "Dangerous Substances & Poisons", + "description": "Filters content related to poisons, toxins, and chemical/biological weapons", + "patterns": [ + { + "pattern": "\\b(poison|toxin)\\s+(recipe|method|create|make)\\b", + "severity": "high", + "description": "Creating poisons or toxins" + }, + { + "pattern": "\\b(ricin|cyanide|arsenic)\\s+(extract|produce|obtain)\\b", + "severity": "critical", + "description": "Specific deadly poisons" + }, + { + "pattern": "\\b(deadly|lethal)\\s+(poison|substance|chemical)\\b", + "severity": "high", + "description": "Lethal substances" + }, + { + "pattern": "\\b(nerve\\s+agent|chemical\\s+weapon)\\s+(create|make|synthesize)\\b", + "severity": "critical", + "description": "Chemical weapons" + }, + { + "pattern": "\\b(biological\\s+weapon|bioweapon)\\s+(create|develop)\\b", + "severity": "critical", + "description": "Biological weapons" + } + ] } - diff --git a/storage/filters/privacy.json b/storage/filters/privacy.json index ef39046..29b836f 100644 --- a/storage/filters/privacy.json +++ b/storage/filters/privacy.json @@ -1,32 +1,31 @@ { - "name": "Privacy Violation & Harassment", - "description": "Filters content related to doxxing, stalking, and harassment", - "patterns": [ - { - "pattern": "\\b(dox|doxx|swat)\\s+(someone|person|user)\\b", - "severity": "high", - "description": "Doxxing or swatting" - }, - { - "pattern": "\\b(find|get|obtain)\\s+(personal\\s+information|home\\s+address|phone\\s+number)\\s+(of|for)\\b", - "severity": "high", - "description": "Obtaining personal information" - }, - { - "pattern": "\\b(stalk|track|locate)\\s+(someone|person)\\s+(online|offline)\\b", - "severity": "high", - "description": "Stalking or tracking" - }, - { - "pattern": "\\bswatting\\s+(guide|method|how\\s+to)\\b", - "severity": "high", - "description": "Swatting guides" - }, - { - "pattern": "\\b(revenge|non-consensual)\\s+(porn|intimate\\s+images)\\b", - "severity": "high", - "description": "Revenge porn" - } - ] + "name": "Privacy Violation & Harassment", + "description": "Filters content related to doxxing, stalking, and harassment", + "patterns": [ + { + "pattern": "\\b(dox|doxx|swat)\\s+(someone|person|user)\\b", + "severity": "high", + "description": "Doxxing or swatting" + }, + { + "pattern": "\\b(find|get|obtain)\\s+(personal\\s+information|home\\s+address|phone\\s+number)\\s+(of|for)\\b", + "severity": "high", + "description": "Obtaining personal information" + }, + { + "pattern": "\\b(stalk|track|locate)\\s+(someone|person)\\s+(online|offline)\\b", + "severity": "high", + "description": "Stalking or tracking" + }, + { + "pattern": "\\bswatting\\s+(guide|method|how\\s+to)\\b", + "severity": "high", + "description": "Swatting guides" + }, + { + "pattern": "\\b(revenge|non-consensual)\\s+(porn|intimate\\s+images)\\b", + "severity": "high", + "description": "Revenge porn" + } + ] } - diff --git a/storage/filters/self-harm.json b/storage/filters/self-harm.json index b7a645e..e8b3230 100644 --- a/storage/filters/self-harm.json +++ b/storage/filters/self-harm.json @@ -1,37 +1,36 @@ { - "name": "Self-Harm & Suicide", - "description": "Filters content related to self-harm and suicide methods", - "patterns": [ - { - "pattern": "\\b(painless|easiest|quickest)\\s+(way\\s+to\\s+)?(commit\\s+suicide|kill\\s+myself|end\\s+my\\s+life)\\b", - "severity": "critical", - "description": "Suicide methods" - }, - { - "pattern": "\\b(suicide|overdose)\\s+(method|technique|guide)\\b", - "severity": "critical", - "description": "Suicide guides" - }, - { - "pattern": "\\b(cut|harm)\\s+myself\\s+(guide|method|how)\\b", - "severity": "high", - "description": "Self-harm methods" - }, - { - "pattern": "\\b(hanging|poisoning|overdose)\\s+(method|instructions)\\b", - "severity": "critical", - "description": "Specific suicide methods" - }, - { - "pattern": "\\b(best|effective)\\s+(suicide|self\\s*harm)\\s+method\\b", - "severity": "critical", - "description": "Seeking effective methods" - }, - { - "pattern": "\\bcommit\\s+suicide\\s+(with|using)\\b", - "severity": "critical", - "description": "Suicide planning" - } - ] + "name": "Self-Harm & Suicide", + "description": "Filters content related to self-harm and suicide methods", + "patterns": [ + { + "pattern": "\\b(painless|easiest|quickest)\\s+(way\\s+to\\s+)?(commit\\s+suicide|kill\\s+myself|end\\s+my\\s+life)\\b", + "severity": "critical", + "description": "Suicide methods" + }, + { + "pattern": "\\b(suicide|overdose)\\s+(method|technique|guide)\\b", + "severity": "critical", + "description": "Suicide guides" + }, + { + "pattern": "\\b(cut|harm)\\s+myself\\s+(guide|method|how)\\b", + "severity": "high", + "description": "Self-harm methods" + }, + { + "pattern": "\\b(hanging|poisoning|overdose)\\s+(method|instructions)\\b", + "severity": "critical", + "description": "Specific suicide methods" + }, + { + "pattern": "\\b(best|effective)\\s+(suicide|self\\s*harm)\\s+method\\b", + "severity": "critical", + "description": "Seeking effective methods" + }, + { + "pattern": "\\bcommit\\s+suicide\\s+(with|using)\\b", + "severity": "critical", + "description": "Suicide planning" + } + ] } - diff --git a/storage/filters/violence.json b/storage/filters/violence.json index b546769..2f488da 100644 --- a/storage/filters/violence.json +++ b/storage/filters/violence.json @@ -1,32 +1,31 @@ { - "name": "Hateful & Violent Content", - "description": "Filters content related to terrorism, mass violence, and hate groups", - "patterns": [ - { - "pattern": "\\b(plan|commit|carry\\s*out)\\s+(mass\\s+shooting|school\\s+shooting|terrorist\\s+attack)\\b", - "severity": "critical", - "description": "Planning mass violence" - }, - { - "pattern": "\\b(genocide|ethnic\\s+cleansing)\\s+(plan|method|justify)\\b", - "severity": "critical", - "description": "Genocide or ethnic cleansing" - }, - { - "pattern": "\\b(neo\\s*nazi|white\\s+supremac|kkk)\\s+(recruit|propaganda|manifesto)\\b", - "severity": "high", - "description": "Hate group propaganda" - }, - { - "pattern": "\\b(terror|terrorist)\\s+(attack|plot|plan)\\s+(execute|carry\\s*out)\\b", - "severity": "critical", - "description": "Terrorist planning" - }, - { - "pattern": "\\b(mass|spree)\\s+(shooting|killing)\\s+(plan|method)\\b", - "severity": "critical", - "description": "Mass killing planning" - } - ] + "name": "Hateful & Violent Content", + "description": "Filters content related to terrorism, mass violence, and hate groups", + "patterns": [ + { + "pattern": "\\b(plan|commit|carry\\s*out)\\s+(mass\\s+shooting|school\\s+shooting|terrorist\\s+attack)\\b", + "severity": "critical", + "description": "Planning mass violence" + }, + { + "pattern": "\\b(genocide|ethnic\\s+cleansing)\\s+(plan|method|justify)\\b", + "severity": "critical", + "description": "Genocide or ethnic cleansing" + }, + { + "pattern": "\\b(neo\\s*nazi|white\\s+supremac|kkk)\\s+(recruit|propaganda|manifesto)\\b", + "severity": "high", + "description": "Hate group propaganda" + }, + { + "pattern": "\\b(terror|terrorist)\\s+(attack|plot|plan)\\s+(execute|carry\\s*out)\\b", + "severity": "critical", + "description": "Terrorist planning" + }, + { + "pattern": "\\b(mass|spree)\\s+(shooting|killing)\\s+(plan|method)\\b", + "severity": "critical", + "description": "Mass killing planning" + } + ] } - diff --git a/storage/filters/weapons.json b/storage/filters/weapons.json index c8b5697..cf1c37e 100644 --- a/storage/filters/weapons.json +++ b/storage/filters/weapons.json @@ -1,42 +1,41 @@ { - "name": "Weapons & Explosives", - "description": "Filters content related to creating weapons, explosives, and illegal firearms", - "patterns": [ - { - "pattern": "\\b(make|build|create|craft|assemble)\\s+(a\\s+)?(bomb|explosive|grenade|molotov|pipe\\s*bomb|ied)\\b", - "severity": "high", - "description": "Creating explosives or bombs" - }, - { - "pattern": "\\b(gun|firearm|rifle|pistol|weapon)\\s+(blueprint|schematic|plan|design)\\b", - "severity": "high", - "description": "Weapon blueprints or designs" - }, - { - "pattern": "\\b(illegal|unregistered|unlicensed)\\s+(weapon|firearm|gun)\\b", - "severity": "medium", - "description": "Illegal weapons" - }, - { - "pattern": "\\b3d\\s*print(ed|ing)?\\s+(gun|firearm|weapon)\\b", - "severity": "high", - "description": "3D printed weapons" - }, - { - "pattern": "\\bammonium\\s*nitrate\\s+(explosive|bomb)\\b", - "severity": "high", - "description": "Explosive materials" - }, - { - "pattern": "\\b(assemble|build)\\s+(ar-?15|ak-?47|semi\\s*auto)\\b", - "severity": "medium", - "description": "Assembling specific firearms" - }, - { - "pattern": "\\b(silencer|suppressor)\\s+(diy|homemade|build|make)\\b", - "severity": "high", - "description": "DIY weapon suppressors" - } - ] + "name": "Weapons & Explosives", + "description": "Filters content related to creating weapons, explosives, and illegal firearms", + "patterns": [ + { + "pattern": "\\b(make|build|create|craft|assemble)\\s+(a\\s+)?(bomb|explosive|grenade|molotov|pipe\\s*bomb|ied)\\b", + "severity": "high", + "description": "Creating explosives or bombs" + }, + { + "pattern": "\\b(gun|firearm|rifle|pistol|weapon)\\s+(blueprint|schematic|plan|design)\\b", + "severity": "high", + "description": "Weapon blueprints or designs" + }, + { + "pattern": "\\b(illegal|unregistered|unlicensed)\\s+(weapon|firearm|gun)\\b", + "severity": "medium", + "description": "Illegal weapons" + }, + { + "pattern": "\\b3d\\s*print(ed|ing)?\\s+(gun|firearm|weapon)\\b", + "severity": "high", + "description": "3D printed weapons" + }, + { + "pattern": "\\bammonium\\s*nitrate\\s+(explosive|bomb)\\b", + "severity": "high", + "description": "Explosive materials" + }, + { + "pattern": "\\b(assemble|build)\\s+(ar-?15|ak-?47|semi\\s*auto)\\b", + "severity": "medium", + "description": "Assembling specific firearms" + }, + { + "pattern": "\\b(silencer|suppressor)\\s+(diy|homemade|build|make)\\b", + "severity": "high", + "description": "DIY weapon suppressors" + } + ] } - diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000..48f99ce --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src/**/*", "tools/**/*", "*.config.*"], + "exclude": ["node_modules", "dist"] +} diff --git a/tsconfig.json b/tsconfig.json index 5726f20..3445638 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,12 @@ { - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "outDir": "dist", - "rootDir": "src", - "strict": true, - "esModuleInterop": true, - "typeRoots": ["./node_modules/@types", "./src/types/*"] - } + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "typeRoots": ["./node_modules/@types", "./src/types/*"] + }, + "exclude": ["*.config.*", "node_modules", "dist", "tools"] }