Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"tabWidth": 4
}
26 changes: 17 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
},
"types": "./dist/index.d.ts",
"default": "./dist/index.mjs"
}
Expand All @@ -17,10 +23,10 @@
"dist"
],
"scripts": {
"build": "npx unbuild --minify",
"dev": "npx unbuild --stub",
"build": "unbuild --minify",
"dev": "unbuild --stub",
"prepare": "pnpm run build",
"test": "npx jiti test.ts",
"test": "jiti test.ts",
"preinstall": "npx only-allow pnpm"
},
"keywords": [
Expand Down Expand Up @@ -48,15 +54,17 @@
],
"author": "memcorrupt",
"license": "MIT",
"engines": {
"node": ">=21.0.0"
},
"engineStrict": false,
"devDependencies": {
"@types/node": "~20.11.30",
"jiti": "^1.21.0",
"typescript": "^5.4.3",
"unbuild": "^2.0.0"
},
"dependencies": {
"undici": "^6.10.2"
},
"dependencies": {},
"repository": {
"type": "git",
"url": "git+https://github.com/Luraph/luraph-node.git"
Expand All @@ -65,4 +73,4 @@
"url": "https://github.com/Luraph/luraph-node/issues"
},
"homepage": "https://github.com/Luraph/luraph-node#readme"
}
}
10 changes: 0 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

170 changes: 104 additions & 66 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,52 @@
import { fetch } from "undici";

const CONTENT_REGEX = /filename[^;=\n]*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/i;
const CONTENT_REGEX =
/filename[^;=\n]*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/i;

export type LuraphOptionValue = string | boolean;

export interface LuraphOptionDependencies {
readonly [target: string]: readonly LuraphOptionValue[]
readonly [target: string]: readonly LuraphOptionValue[];
}

export interface LuraphOptionInfo {
readonly name: string;
readonly description: string;
export type LuraphOptionInfo = Readonly<{
name: string;
description: string;

readonly tier: "CUSTOMER_ONLY" | "PREMIUM_ONLY" | "ADMIN_ONLY";
readonly type: "CHECKBOX" | "DROPDOWN" | "TEXT";
tier: "CUSTOMER_ONLY" | "PREMIUM_ONLY" | "ADMIN_ONLY";
type: "CHECKBOX" | "DROPDOWN" | "TEXT";

//empty `[]` if `type !== DROPDOWN`
readonly choices: readonly string[];
/**
* empty `[]` if `type !== DROPDOWN`
*/
choices: readonly string[];

readonly required: boolean;
readonly dependencies?: LuraphOptionDependencies;
}
required: boolean;
dependencies?: LuraphOptionDependencies;
}>;

export interface LuraphNode {
readonly cpuUsage: number;
readonly options: {
readonly [key: string]: LuraphOptionInfo;
export type LuraphNode = Readonly<{
cpuUsage: number;
options: {
readonly [key: string]: LuraphOptionInfo;
};
}
}>;

export interface LuraphOptionList {
[key: string]: LuraphOptionValue;
}

export interface LuraphError {
readonly param?: string;
readonly message: string;
}
export type LuraphError = Readonly<{
param?: string;
message: string;
}>;

export class LuraphException extends Error {
public readonly errors: LuraphError[];

constructor(payload: LuraphError[]){
constructor(payload: LuraphError[]) {
let errorMsg = payload
.map(({param, message}) => param ? `${param}: ${message}` : message)
.map(({ param, message }) =>
param ? `${param}: ${message}` : message
)
.join(" | ");

super(`${errorMsg}`);
Expand All @@ -60,80 +63,115 @@ export class Luraph {
this.apiKey = apiKey;
}

private async doRequest(url: string, isPost = false, body: object | undefined = undefined, rawResponse = false) {
const req = await fetch(
new URL(url, "https://api.lura.ph/v1/"),
{
method: isPost ? "POST" : "GET",
headers: {
"Luraph-API-Key": this.apiKey,
"Content-Type": "application/json"
},
body: JSON.stringify(body)
}
);

private async request<
Parsed extends Object,
isRaw extends boolean = false,
R = isRaw extends true ? Response : Parsed
>(
url: string,
isPost = false,
body: object | undefined = undefined,
rawResponse: isRaw = false as isRaw
): Promise<R> {
const req = await fetch(new URL(url, "https://api.lura.ph/v1/"), {
method: isPost ? "POST" : "GET",
headers: {
"Luraph-API-Key": this.apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});

//raw responses
if(rawResponse && req.ok)
return req;
// @ts-ignore type can't be inferred even though in this branch rawResponse is true
if (rawResponse && req.ok) return req;

const rawResp = await req.text();
const resp = rawResp.length ? JSON.parse(rawResp) : {};

if(resp.warnings){
for(const warning of resp.warnings){
if (resp.warnings) {
for (const warning of resp.warnings) {
console.warn(`Luraph API warning: ${warning}`);
}
}

if(req.ok){
return resp;
}else{
let errors = (resp as any)?.errors;
if(!errors)
errors = [{
message: "An unknown error occurred",
rawBody: resp
}];

if (req.ok) {
return resp;
} else {
let errors = (
resp as {
errors: (LuraphError & { rawBody?: string })[];
}
)?.errors;
if (!errors)
errors = [
{
message: "An unknown error occurred",
rawBody: resp,
},
];

throw new LuraphException(errors);
}
}
}

public getNodes() {
return this.doRequest("obfuscate/nodes") as Promise<{ nodes: {[nodeId: string]: LuraphNode}; recommendedId: string | null }>;
return this.request<{
nodes: { [nodeId: string]: LuraphNode };
recommendedId: string | null;
}>("obfuscate/nodes");
}

public createNewJob(node: string, script: string, fileName: string, options: LuraphOptionList, useTokens = false, enforceSettings = false) {
public createNewJob(
node: string,
script: string,
fileName: string,
options: LuraphOptionList,
useTokens = false,
enforceSettings = false
) {
script = Buffer.from(script).toString("base64");
return this.doRequest("obfuscate/new", true, {
return this.request<{ jobId: string }>("obfuscate/new", true, {
node,
script,
fileName,
options,
useTokens,
enforceSettings
}) as Promise<{ jobId: string }>;
enforceSettings,
});
}

public async getJobStatus(jobId: string) {
const data = await this.doRequest(`obfuscate/status/${jobId}`) as { error: string | null };
const data = await this.request<{
error: string | null;
}>(`obfuscate/status/${jobId}`);

return {
success: !data.error,
error: data.error
} as ({ success: true, error: null } | { success: false, error: string });
error: data.error,
} as { success: true; error: null } | { success: false; error: string };
}

public async downloadResult(jobId: string) {
const req = await this.doRequest(`obfuscate/download/${jobId}`, false, undefined, true);
const fileName = CONTENT_REGEX.exec(req.headers.get("content-disposition"))?.[2] || "script-obfuscated.lua";
const req = await this.request(
`obfuscate/download/${jobId}`,
false,
undefined,
true
);

const fileName =
CONTENT_REGEX.exec(
req.headers.get("content-disposition") ?? ""
)?.[2] || "script-obfuscated.lua";

const data = await req.text();

return {
data,
fileName
fileName,
};
}
};
}

export default Luraph;
export default Luraph;