mirror of
https://github.com/ragestudio/relic.git
synced 2025-06-09 10:34:18 +00:00
merge from local
This commit is contained in:
parent
f48c1e6524
commit
93329a7328
@ -1,6 +0,0 @@
|
||||
out
|
||||
dist
|
||||
pnpm-lock.yaml
|
||||
LICENSE.md
|
||||
tsconfig.json
|
||||
tsconfig.*.json
|
@ -1,4 +0,0 @@
|
||||
singleQuote: false
|
||||
semi: false
|
||||
printWidth: 100
|
||||
trailingComma: none
|
13
.vscode/settings.json
vendored
13
.vscode/settings.json
vendored
@ -7,5 +7,16 @@
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
},
|
||||
"cSpell.words": [
|
||||
"admzip",
|
||||
"APPDATA",
|
||||
"catched",
|
||||
"execa",
|
||||
"rclone",
|
||||
"sevenzip",
|
||||
"unzipper",
|
||||
"upath",
|
||||
"userdata"
|
||||
]
|
||||
}
|
||||
|
9
TODO.md
9
TODO.md
@ -1,9 +0,0 @@
|
||||
[] auto install java on setup
|
||||
[x] support install ask configs
|
||||
[] DEVLOGS
|
||||
[] improve package last task view (statusText)
|
||||
[] show git clone status
|
||||
|
||||
[] fix app update modal
|
||||
[] fix update removes "options.txt"
|
||||
[] improve child process on management
|
78
package.json
78
package.json
@ -1,75 +1,13 @@
|
||||
{
|
||||
"name": "rs-bundler",
|
||||
"version": "0.15.0",
|
||||
"description": "RageStudio Bundler Utility GUI",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "RageStudio",
|
||||
"name": "@ragestudio/relic",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"repository": "https://github.com/srgooglo/rs_bundler",
|
||||
"author": "SrGooglo <srgooglo@ragestudio.net>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
||||
"start": "electron-vite preview",
|
||||
"dev": "electron-vite dev",
|
||||
"build": "electron-vite build",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"pack:win": "electron-builder --win --config",
|
||||
"pack:mac": "electron-builder --mac --config",
|
||||
"pack:linux": "electron-builder --linux --config",
|
||||
"build:win": "npm run build && npm run pack:win",
|
||||
"build:mac": "npm run build && npm run pack:mac",
|
||||
"build:linux": "npm run build && npm run pack:linux"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron-toolkit/preload": "^2.0.0",
|
||||
"@electron-toolkit/utils": "^2.0.0",
|
||||
"@getstation/electron-google-oauth2": "^14.0.0",
|
||||
"@imjs/electron-differential-updater": "^5.1.7",
|
||||
"@loadable/component": "^5.16.3",
|
||||
"@ragestudio/hermes": "^0.1.1",
|
||||
"adm-zip": "^0.5.10",
|
||||
"antd": "^5.13.2",
|
||||
"checksum": "^1.0.0",
|
||||
"classnames": "^2.3.2",
|
||||
"electron-differential-updater": "^4.3.2",
|
||||
"electron-is-dev": "^2.0.0",
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "^6.1.1",
|
||||
"googleapis": "^105.0.0",
|
||||
"got": "11.8.3",
|
||||
"human-format": "^1.2.0",
|
||||
"less": "^4.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"merge-stream": "^2.0.0",
|
||||
"node-7z": "^3.0.0",
|
||||
"open": "8.4.2",
|
||||
"progress-stream": "^2.0.0",
|
||||
"protocol-registry": "^1.4.1",
|
||||
"react-icons": "^4.11.0",
|
||||
"react-router-dom": "6.6.2",
|
||||
"react-spinners": "^0.13.8",
|
||||
"react-spring": "^9.7.3",
|
||||
"react-motion": "0.5.2",
|
||||
"request": "^2.88.2",
|
||||
"rimraf": "^5.0.5",
|
||||
"signal-exit": "^4.1.0",
|
||||
"unzipper": "^0.10.14",
|
||||
"upath": "^2.0.1",
|
||||
"uuid": "^9.0.1",
|
||||
"which": "^4.0.0",
|
||||
"winreg": "^1.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/eslint-config": "^1.0.1",
|
||||
"@electron-toolkit/eslint-config-prettier": "^1.0.1",
|
||||
"@vitejs/plugin-react": "^4.0.4",
|
||||
"electron": "^25.6.0",
|
||||
"electron-builder": "^24.6.3",
|
||||
"electron-vite": "^1.0.27",
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"prettier": "^3.0.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"vite": "^4.4.9"
|
||||
"postinstall": "node scripts/postinstall.js"
|
||||
}
|
||||
}
|
||||
|
2
packages/cli/bin
Executable file
2
packages/cli/bin
Executable file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
require("./dist/index.js")
|
24
packages/cli/package.json
Normal file
24
packages/cli/package.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "@ragestudio/relic-cli",
|
||||
"version": "0.16.0",
|
||||
"license": "MIT",
|
||||
"author": "RageStudio",
|
||||
"description": "RageStudio Relic, yet another package manager.",
|
||||
"main": "./dist/index.js",
|
||||
"bin": {
|
||||
"relic": "./bin.js"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "hermes-node ./src/index.js",
|
||||
"build": "hermes build"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": "^12.0.0",
|
||||
"glob": "^10.3.12",
|
||||
"lowdb": "^7.0.1",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ragestudio/hermes": "^0.1.1"
|
||||
}
|
||||
}
|
166
packages/cli/src/index.js
Normal file
166
packages/cli/src/index.js
Normal file
@ -0,0 +1,166 @@
|
||||
import RelicCore from "@ragestudio/relic-core"
|
||||
import { program, Command, Argument } from "commander"
|
||||
|
||||
import pkg from "../package.json"
|
||||
|
||||
const commands = [
|
||||
{
|
||||
cmd: "install",
|
||||
description: "Install a package manifest from a path or URL",
|
||||
arguments: [
|
||||
{
|
||||
name: "package_manifest",
|
||||
description: "Path or URL to a package manifest",
|
||||
}
|
||||
],
|
||||
fn: async (package_manifest, options) => {
|
||||
await core.initialize()
|
||||
|
||||
return await core.package.install(package_manifest, options)
|
||||
}
|
||||
},
|
||||
{
|
||||
cmd: "run",
|
||||
description: "Execute a package",
|
||||
arguments: [
|
||||
{
|
||||
name: "id",
|
||||
description: "The id of the package to execute",
|
||||
}
|
||||
],
|
||||
fn: async (pkg_id, options) => {
|
||||
await core.initialize()
|
||||
|
||||
return await core.package.execute(pkg_id, options)
|
||||
}
|
||||
},
|
||||
{
|
||||
cmd: "update",
|
||||
description: "Update a package",
|
||||
arguments: [
|
||||
{
|
||||
name: "id",
|
||||
description: "The id of the package to update",
|
||||
}
|
||||
],
|
||||
fn: async (pkg_id, options) => {
|
||||
await core.initialize()
|
||||
|
||||
return await core.package.update(pkg_id, options)
|
||||
}
|
||||
},
|
||||
{
|
||||
cmd: "uninstall",
|
||||
description: "Uninstall a package",
|
||||
arguments: [
|
||||
{
|
||||
name: "id",
|
||||
description: "The id of the package to uninstall",
|
||||
}
|
||||
],
|
||||
fn: async (pkg_id, options) => {
|
||||
await core.initialize()
|
||||
|
||||
return await core.package.uninstall(pkg_id, options)
|
||||
}
|
||||
},
|
||||
{
|
||||
cmd: "apply",
|
||||
description: "Apply changes to a installed package",
|
||||
arguments: [
|
||||
{
|
||||
name: "id",
|
||||
description: "The id of the package to apply changes to",
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: "add_patches",
|
||||
description: "Add patches to the package",
|
||||
},
|
||||
{
|
||||
name: "remove_patches",
|
||||
description: "Remove patches from the package",
|
||||
},
|
||||
],
|
||||
fn: async (pkg_id, options) => {
|
||||
await core.initialize()
|
||||
|
||||
return await core.package.apply(pkg_id, options)
|
||||
}
|
||||
},
|
||||
{
|
||||
cmd: "list",
|
||||
description: "List installed package manifests",
|
||||
fn: async () => {
|
||||
await core.initialize()
|
||||
|
||||
return console.log(await core.package.list())
|
||||
}
|
||||
},
|
||||
{
|
||||
cmd: "open-path",
|
||||
description: "Open the base path or a package path",
|
||||
options: [
|
||||
{
|
||||
name: "pkg_id",
|
||||
description: "Path to open",
|
||||
}
|
||||
],
|
||||
fn: async (options) => {
|
||||
await core.initialize()
|
||||
|
||||
await core.openPath(options.pkg_id)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
async function main() {
|
||||
global.core = new RelicCore()
|
||||
|
||||
program
|
||||
.name(pkg.name)
|
||||
.description(pkg.description)
|
||||
.version(pkg.version)
|
||||
|
||||
for await (const command of commands) {
|
||||
const cmd = new Command(command.cmd).action(command.fn)
|
||||
|
||||
if (command.description) {
|
||||
cmd.description(command.description)
|
||||
}
|
||||
|
||||
if (Array.isArray(command.arguments)) {
|
||||
for await (const argument of command.arguments) {
|
||||
if (typeof argument === "string") {
|
||||
cmd.addArgument(new Argument(argument))
|
||||
} else {
|
||||
const arg = new Argument(argument.name, argument.description)
|
||||
|
||||
if (argument.default) {
|
||||
arg.default(argument.default)
|
||||
}
|
||||
|
||||
cmd.addArgument(arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(command.options)) {
|
||||
for await (const option of command.options) {
|
||||
if (typeof option === "string") {
|
||||
cmd.option(option)
|
||||
} else {
|
||||
cmd.option(option.name, option.description, option.default)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
program.addCommand(cmd)
|
||||
}
|
||||
|
||||
program.parse()
|
||||
}
|
||||
|
||||
|
||||
main()
|
38
packages/core/package.json
Normal file
38
packages/core/package.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@ragestudio/relic-core",
|
||||
"version": "0.16.0",
|
||||
"license": "MIT",
|
||||
"author": "RageStudio",
|
||||
"description": "RageStudio Relic, yet another package manager.",
|
||||
"main": "./dist/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "hermes build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@foxify/events": "^2.1.0",
|
||||
"adm-zip": "^0.5.12",
|
||||
"axios": "^1.6.8",
|
||||
"checksum": "^1.0.0",
|
||||
"cli-color": "^2.0.4",
|
||||
"cli-progress": "^3.12.0",
|
||||
"extends-classes": "^1.0.5",
|
||||
"googleapis": "^134.0.0",
|
||||
"human-format": "^1.2.0",
|
||||
"isolated-vm": "^4.7.2",
|
||||
"merge-stream": "^2.0.0",
|
||||
"module-alias": "^2.2.3",
|
||||
"node-7z": "^3.0.0",
|
||||
"open": "8.4.2",
|
||||
"progress-stream": "^2.0.0",
|
||||
"request": "^2.88.2",
|
||||
"rimraf": "^5.0.5",
|
||||
"unzipper": "^0.10.14",
|
||||
"upath": "^2.0.1",
|
||||
"uuid": "^9.0.1",
|
||||
"winston": "^3.13.0"
|
||||
}
|
||||
}
|
34
packages/core/src/classes/ManifestConfig.js
Normal file
34
packages/core/src/classes/ManifestConfig.js
Normal file
@ -0,0 +1,34 @@
|
||||
import DB from "../db"
|
||||
|
||||
export default class ManifestConfigManager {
|
||||
constructor(pkg_id) {
|
||||
this.pkg_id = pkg_id
|
||||
this.config = null
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
const pkg = await DB.getPackages(this.pkg_id) ?? {}
|
||||
|
||||
this.config = pkg.config
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
this.config[key] = value
|
||||
|
||||
DB.updatePackageById(pkg_id, { config: this.config })
|
||||
|
||||
return this.config
|
||||
}
|
||||
|
||||
get(key) {
|
||||
return this.config[key]
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
delete this.config[key]
|
||||
|
||||
DB.updatePackageById(pkg_id, { config: this.config })
|
||||
|
||||
return this.config
|
||||
}
|
||||
}
|
117
packages/core/src/classes/PatchManager.js
Normal file
117
packages/core/src/classes/PatchManager.js
Normal file
@ -0,0 +1,117 @@
|
||||
import fs from "node:fs"
|
||||
|
||||
import GenericSteps from "../generic_steps"
|
||||
import parseStringVars from "../utils/parseStringVars"
|
||||
|
||||
export default class PatchManager {
|
||||
constructor(pkg, manifest) {
|
||||
this.pkg = pkg
|
||||
this.manifest = manifest
|
||||
|
||||
this.log = Logger.child({ service: `PATCH-MANAGER|${pkg.id}` })
|
||||
}
|
||||
|
||||
async get(patch) {
|
||||
if (!this.manifest.patches) {
|
||||
return []
|
||||
}
|
||||
|
||||
let list = []
|
||||
|
||||
if (typeof patch === "undefined") {
|
||||
list = this.manifest.patches
|
||||
} else {
|
||||
list = this.manifest.patches.find((p) => p.id === patch.id)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
async patch(patch) {
|
||||
const list = await this.get(patch)
|
||||
|
||||
for await (let patch of list) {
|
||||
global._relic_eventBus.emit(`pkg:update:state:${this.pkg.id}`, {
|
||||
status_text: `Applying patch [${patch.id}]...`,
|
||||
})
|
||||
|
||||
this.log.info(`Applying patch [${patch.id}]...`)
|
||||
|
||||
if (Array.isArray(patch.additions)) {
|
||||
this.log.info(`Applying ${patch.additions.length} Additions...`)
|
||||
|
||||
for await (let addition of patch.additions) {
|
||||
this.log.info(`Applying addition [${addition.id}]...`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${this.pkg.id}`, {
|
||||
status_text: `Applying addition [${additions.id}]...`,
|
||||
})
|
||||
|
||||
// resolve patch file
|
||||
addition.file = await parseStringVars(addition.file, this.pkg)
|
||||
|
||||
if (fs.existsSync(addition.file)) {
|
||||
this.log.info(`Addition [${addition.file}] already exists. Skipping...`)
|
||||
continue
|
||||
}
|
||||
|
||||
await GenericSteps(this.pkg, addition.steps, this.log)
|
||||
}
|
||||
}
|
||||
|
||||
pkg.applied_patches.push(patch.id)
|
||||
}
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${this.pkg.id}`, {
|
||||
status_text: `${list.length} Patches applied`,
|
||||
})
|
||||
|
||||
this.log.info(`${list.length} Patches applied`)
|
||||
|
||||
return this.pkg
|
||||
}
|
||||
|
||||
async remove(patch) {
|
||||
const list = await this.get(patch)
|
||||
|
||||
for await (let patch of list) {
|
||||
global._relic_eventBus.emit(`pkg:update:state:${this.pkg.id}`, {
|
||||
status_text: `Removing patch [${patch.id}]...`,
|
||||
})
|
||||
|
||||
Log.info(`Removing patch [${patch.id}]...`)
|
||||
|
||||
if (Array.isArray(patch.additions)) {
|
||||
this.log.info(`Removing ${patch.additions.length} Additions...`)
|
||||
|
||||
for await (let addition of patch.additions) {
|
||||
this.log.info(`Removing addition [${addition.id}]...`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${this.pkg.id}`, {
|
||||
status_text: `Removing addition [${additions.id}]...`,
|
||||
})
|
||||
|
||||
addition.file = await parseStringVars(addition.file, this.pkg)
|
||||
|
||||
if (!fs.existsSync(addition.file)) {
|
||||
continue
|
||||
}
|
||||
|
||||
await fs.promises.unlink(addition.file)
|
||||
}
|
||||
}
|
||||
|
||||
pkg.applied_patches = pkg.applied_patches.filter((p) => {
|
||||
return p !== patch.id
|
||||
})
|
||||
}
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${this.pkg.id}`, {
|
||||
status_text: `${list.length} Patches removed`,
|
||||
})
|
||||
|
||||
this.log.info(`${list.length} Patches removed`)
|
||||
|
||||
return this.pkg
|
||||
}
|
||||
}
|
113
packages/core/src/db.js
Normal file
113
packages/core/src/db.js
Normal file
@ -0,0 +1,113 @@
|
||||
import { JSONFilePreset } from "./libraries/lowdb/presets/node"
|
||||
import Vars from "./vars"
|
||||
import pkg from "../package.json"
|
||||
import fs from "node:fs"
|
||||
import lodash from "lodash"
|
||||
|
||||
export default class DB {
|
||||
static get defaultRoot() {
|
||||
return {
|
||||
created_at_version: pkg.version,
|
||||
packages: [],
|
||||
}
|
||||
}
|
||||
|
||||
static defaultPackageState({
|
||||
id,
|
||||
name,
|
||||
version,
|
||||
install_path,
|
||||
description,
|
||||
license,
|
||||
last_status,
|
||||
remote_manifest,
|
||||
local_manifest,
|
||||
config,
|
||||
}) {
|
||||
return {
|
||||
id: id,
|
||||
name: name,
|
||||
version: version,
|
||||
install_path: install_path,
|
||||
description: description,
|
||||
license: license ?? "unlicensed",
|
||||
local_manifest: local_manifest ?? null,
|
||||
remote_manifest: remote_manifest ?? null,
|
||||
applied_patches: [],
|
||||
config: typeof config === "object" ? config : {},
|
||||
last_status: last_status ?? "installing",
|
||||
last_update: null,
|
||||
installed_at: null,
|
||||
}
|
||||
}
|
||||
|
||||
static async withDB() {
|
||||
return await JSONFilePreset(Vars.db_path, DB.defaultRoot)
|
||||
}
|
||||
|
||||
static async initialize() {
|
||||
await this.cleanOrphans()
|
||||
}
|
||||
|
||||
static async cleanOrphans() {
|
||||
const list = await this.getPackages()
|
||||
|
||||
for (const pkg of list) {
|
||||
if (!fs.existsSync(pkg.install_path)) {
|
||||
await this.deletePackage(pkg.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async getPackages(pkg_id) {
|
||||
const db = await this.withDB()
|
||||
|
||||
if (pkg_id) {
|
||||
return db.data["packages"].find((i) => i.id === pkg_id)
|
||||
}
|
||||
|
||||
return db.data["packages"]
|
||||
}
|
||||
|
||||
static async writePackage(pkg) {
|
||||
const db = await this.withDB()
|
||||
|
||||
await db.update((data) => {
|
||||
const prevIndex = data["packages"].findIndex((i) => i.id === pkg.id)
|
||||
|
||||
if (prevIndex !== -1) {
|
||||
data["packages"][prevIndex] = pkg
|
||||
} else {
|
||||
data["packages"].push(pkg)
|
||||
}
|
||||
|
||||
return data
|
||||
})
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
static async updatePackageById(pkg_id, obj) {
|
||||
const pkg = await this.getPackages(pkg_id)
|
||||
|
||||
if (!pkg) {
|
||||
throw new Error("Package not found")
|
||||
}
|
||||
|
||||
pkg = lodash.merge(pkg, obj)
|
||||
|
||||
return await this.writePackage(pkg)
|
||||
}
|
||||
|
||||
static async deletePackage(pkg_id) {
|
||||
const db = await this.withDB()
|
||||
|
||||
await db.update((data) => {
|
||||
data["packages"] = data["packages"].filter((i) => i.id !== pkg_id)
|
||||
|
||||
return data
|
||||
})
|
||||
|
||||
return pkg_id
|
||||
}
|
||||
}
|
46
packages/core/src/generic_steps/git_clone.js
Normal file
46
packages/core/src/generic_steps/git_clone.js
Normal file
@ -0,0 +1,46 @@
|
||||
import path from "node:path"
|
||||
import fs from "node:fs"
|
||||
import upath from "upath"
|
||||
import { execa } from "../libraries/execa"
|
||||
|
||||
import Vars from "../vars"
|
||||
|
||||
export default async (pkg, step) => {
|
||||
if (!step.path) {
|
||||
step.path = `.`
|
||||
}
|
||||
|
||||
const Log = Logger.child({ service: `GIT|${pkg.id}` })
|
||||
|
||||
const gitCMD = fs.existsSync(Vars.git_path) ? `${Vars.git_path}` : "git"
|
||||
const final_path = upath.normalizeSafe(path.resolve(pkg.install_path, step.path))
|
||||
|
||||
if (!fs.existsSync(final_path)) {
|
||||
fs.mkdirSync(final_path, { recursive: true })
|
||||
}
|
||||
|
||||
Log.info(`Cloning from [${step.url}]`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Cloning from [${step.url}]...`,
|
||||
})
|
||||
|
||||
const args = [
|
||||
"clone",
|
||||
//`--depth ${step.depth ?? 1}`,
|
||||
//"--filter=blob:none",
|
||||
//"--filter=tree:0",
|
||||
"--recurse-submodules",
|
||||
"--remote-submodules",
|
||||
step.url,
|
||||
final_path,
|
||||
]
|
||||
|
||||
await execa(gitCMD, args, {
|
||||
cwd: final_path,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
})
|
||||
|
||||
return pkg
|
||||
}
|
30
packages/core/src/generic_steps/git_pull.js
Normal file
30
packages/core/src/generic_steps/git_pull.js
Normal file
@ -0,0 +1,30 @@
|
||||
import path from "node:path"
|
||||
import fs from "node:fs"
|
||||
import { execa } from "../libraries/execa"
|
||||
|
||||
import Vars from "../vars"
|
||||
|
||||
export default async (pkg, step) => {
|
||||
if (!step.path) {
|
||||
step.path = `.`
|
||||
}
|
||||
|
||||
const Log = Logger.child({ service: `GIT|${pkg.id}` })
|
||||
|
||||
const gitCMD = fs.existsSync(Vars.git_path) ? `${Vars.git_path}` : "git"
|
||||
const _path = path.resolve(pkg.install_path, step.path)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Pulling...`,
|
||||
})
|
||||
|
||||
Log.info(`Pulling from HEAD...`)
|
||||
|
||||
await execa(gitCMD, ["pull", "--rebase"], {
|
||||
cwd: _path,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
})
|
||||
|
||||
return pkg
|
||||
}
|
77
packages/core/src/generic_steps/git_reset.js
Normal file
77
packages/core/src/generic_steps/git_reset.js
Normal file
@ -0,0 +1,77 @@
|
||||
import path from "node:path"
|
||||
import fs from "node:fs"
|
||||
import { execa } from "../libraries/execa"
|
||||
|
||||
import git_pull from "./git_pull"
|
||||
import Vars from "../vars"
|
||||
|
||||
export default async (pkg, step) => {
|
||||
if (!step.path) {
|
||||
step.path = `.`
|
||||
}
|
||||
|
||||
const Log = Logger.child({ service: `GIT|${pkg.id}` })
|
||||
|
||||
const gitCMD = fs.existsSync(Vars.git_path) ? `${Vars.git_path}` : "git"
|
||||
|
||||
const _path = path.resolve(pkg.install_path, step.path)
|
||||
const from = step.from ?? "HEAD"
|
||||
|
||||
if (!fs.existsSync(_path)) {
|
||||
fs.mkdirSync(_path, { recursive: true })
|
||||
}
|
||||
|
||||
Log.info(`Fetching from origin`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Fetching from origin...`,
|
||||
})
|
||||
|
||||
// fetch from origin
|
||||
await execa(gitCMD, ["fetch", "origin"], {
|
||||
cwd: _path,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
})
|
||||
|
||||
Log.info(`Cleaning untracked files...`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Cleaning untracked files...`,
|
||||
})
|
||||
|
||||
await execa(gitCMD, ["clean", "-df"], {
|
||||
cwd: _path,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
})
|
||||
|
||||
Log.info(`Resetting to ${from}`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Resetting to ${from}`,
|
||||
})
|
||||
|
||||
await execa(gitCMD, ["reset", "--hard", from], {
|
||||
cwd: _path,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
})
|
||||
|
||||
// pull the latest
|
||||
await git_pull(pkg, step)
|
||||
|
||||
Log.info(`Checkout to HEAD`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Checkout to HEAD`,
|
||||
})
|
||||
|
||||
await execa(gitCMD, ["checkout", "HEAD"], {
|
||||
cwd: _path,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
})
|
||||
|
||||
return pkg
|
||||
}
|
62
packages/core/src/generic_steps/http.js
Normal file
62
packages/core/src/generic_steps/http.js
Normal file
@ -0,0 +1,62 @@
|
||||
import path from "node:path"
|
||||
import fs from "node:fs"
|
||||
import os from "node:os"
|
||||
|
||||
import downloadHttpFile from "../helpers/downloadHttpFile"
|
||||
import parseStringVars from "../utils/parseStringVars"
|
||||
import extractFile from "../utils/extractFile"
|
||||
|
||||
export default async (pkg, step, logger) => {
|
||||
if (!step.path) {
|
||||
step.path = `./${path.basename(step.url)}`
|
||||
}
|
||||
|
||||
step.path = await parseStringVars(step.path, pkg)
|
||||
|
||||
let _path = path.resolve(pkg.install_path, step.path)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status: "loading",
|
||||
statusText: `Downloading [${step.url}]`,
|
||||
})
|
||||
|
||||
logger.info(`Downloading [${step.url} to ${_path}]`)
|
||||
|
||||
if (step.tmp) {
|
||||
_path = path.resolve(os.tmpdir(), String(new Date().getTime()), path.basename(step.url))
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.resolve(_path, ".."), { recursive: true })
|
||||
|
||||
await downloadHttpFile(step.url, _path, (progress) => {
|
||||
global._relic_eventBus(`pkg:update:state:${pkg.id}`, {
|
||||
statusText: `Downloaded ${progress.transferredString} / ${progress.totalString} | ${progress.speedString}/s`,
|
||||
})
|
||||
})
|
||||
|
||||
logger.info(`Downloaded finished.`)
|
||||
|
||||
if (step.extract) {
|
||||
if (typeof step.extract === "string") {
|
||||
step.extract = path.resolve(pkg.install_path, step.extract)
|
||||
} else {
|
||||
step.extract = path.resolve(pkg.install_path, ".")
|
||||
}
|
||||
|
||||
global._relic_eventBus(`pkg:update:state:${pkg.id}`, {
|
||||
statusText: `Extracting bundle...`,
|
||||
})
|
||||
|
||||
await extractFile(_path, step.extract)
|
||||
|
||||
if (step.deleteAfterExtract !== false) {
|
||||
logger.info(`Deleting temporal file [${_path}]...`)
|
||||
|
||||
global._relic_eventBus(`pkg:update:state:${pkg.id}`, {
|
||||
statusText: `Deleting temporal files...`,
|
||||
})
|
||||
|
||||
await fs.promises.rm(_path, { recursive: true })
|
||||
}
|
||||
}
|
||||
}
|
42
packages/core/src/generic_steps/index.js
Normal file
42
packages/core/src/generic_steps/index.js
Normal file
@ -0,0 +1,42 @@
|
||||
import ISM_GIT_CLONE from "./git_clone"
|
||||
import ISM_GIT_PULL from "./git_pull"
|
||||
import ISM_GIT_RESET from "./git_reset"
|
||||
import ISM_HTTP from "./http"
|
||||
|
||||
const InstallationStepsMethods = {
|
||||
git_clone: ISM_GIT_CLONE,
|
||||
git_pull: ISM_GIT_PULL,
|
||||
git_reset: ISM_GIT_RESET,
|
||||
http_file: ISM_HTTP,
|
||||
}
|
||||
|
||||
const StepsOrders = [
|
||||
"git_clones",
|
||||
"git_pull",
|
||||
"git_reset",
|
||||
"http_file",
|
||||
]
|
||||
|
||||
export default async function processGenericSteps(pkg, steps, logger = Logger) {
|
||||
logger.info(`Processing generic steps...`)
|
||||
|
||||
if (steps.length === 0) {
|
||||
return pkg
|
||||
}
|
||||
|
||||
steps = steps.sort((a, b) => {
|
||||
return StepsOrders.indexOf(a.type) - StepsOrders.indexOf(b.type)
|
||||
})
|
||||
|
||||
for await (let step of steps) {
|
||||
step.type = step.type.toLowerCase()
|
||||
|
||||
if (!InstallationStepsMethods[step.type]) {
|
||||
throw new Error(`Unknown step: ${step.type}`)
|
||||
}
|
||||
|
||||
await InstallationStepsMethods[step.type](pkg, step, logger)
|
||||
}
|
||||
|
||||
return pkg
|
||||
}
|
82
packages/core/src/handlers/apply.js
Normal file
82
packages/core/src/handlers/apply.js
Normal file
@ -0,0 +1,82 @@
|
||||
import ManifestReader from "../manifest/reader"
|
||||
import ManifestVM from "../manifest/vm"
|
||||
import DB from "../db"
|
||||
|
||||
const BaseLog = Logger.child({ service: "APPLIER" })
|
||||
|
||||
function findPatch(manifest, changes, mustBeInstalled) {
|
||||
return manifest.patches
|
||||
.filter((patch) => {
|
||||
const patchID = patch.id
|
||||
|
||||
if (typeof changes.patches[patchID] === "undefined") {
|
||||
return false
|
||||
}
|
||||
|
||||
if (mustBeInstalled === true && !manifest.applied_patches.includes(patch.id) && changes.patches[patchID] === true) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (mustBeInstalled === false && manifest.applied_patches.includes(patch.id) && changes.patches[patchID] === false) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
export default async function apply(pkg_id, changes = {}) {
|
||||
try {
|
||||
let pkg = await DB.getPackages(pkg_id)
|
||||
|
||||
if (!pkg) {
|
||||
BaseLog.error(`Package not found [${pkg_id}]`)
|
||||
return null
|
||||
}
|
||||
|
||||
let manifest = await ManifestReader(pkg.local_manifest)
|
||||
manifest = await ManifestVM(ManifestRead.code)
|
||||
|
||||
const Log = Logger.child({ service: `APPLIER|${pkg.id}` })
|
||||
|
||||
Log.info(`Applying changes to package...`)
|
||||
|
||||
if (changes.patches) {
|
||||
if (!Array.isArray(pkg.applied_patches)) {
|
||||
pkg.applied_patches = []
|
||||
}
|
||||
|
||||
const patches = new PatchManager(pkg, manifest)
|
||||
|
||||
await patches.remove(findPatch(manifest, changes, false))
|
||||
await patches.patch(findPatch(manifest, changes, true))
|
||||
}
|
||||
|
||||
if (changes.config) {
|
||||
Log.info(`Applying config to package...`)
|
||||
|
||||
if (Object.keys(changes.config).length !== 0) {
|
||||
Object.entries(changes.config).forEach(([key, value]) => {
|
||||
pkg.config[key] = value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
await DB.writePackage(pkg)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
state: "All changes applied",
|
||||
})
|
||||
|
||||
Log.info(`All changes applied to package.`)
|
||||
|
||||
return pkg
|
||||
} catch (error) {
|
||||
global._relic_eventBus.emit(`pkg:${pkg_id}:error`, error)
|
||||
|
||||
BaseLog.error(`Failed to apply changes to package [${pkg_id}]`, error)
|
||||
BaseLog.error(error.stack)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
64
packages/core/src/handlers/execute.js
Normal file
64
packages/core/src/handlers/execute.js
Normal file
@ -0,0 +1,64 @@
|
||||
import fs from "node:fs"
|
||||
|
||||
import DB from "../db"
|
||||
import SetupHelper from "../helpers/setup"
|
||||
import ManifestReader from "../manifest/reader"
|
||||
import ManifestVM from "../manifest/vm"
|
||||
import parseStringVars from "../utils/parseStringVars"
|
||||
import { execa } from "../libraries/execa"
|
||||
|
||||
const BaseLog = Logger.child({ service: "EXECUTER" })
|
||||
|
||||
export default async function execute(pkg_id, { useRemote = false, force = false } = {}) {
|
||||
try {
|
||||
const pkg = await DB.getPackages(pkg_id)
|
||||
|
||||
if (!pkg) {
|
||||
BaseLog.info(`Package not found [${pkg_id}]`)
|
||||
return false
|
||||
}
|
||||
|
||||
await SetupHelper()
|
||||
|
||||
const manifestPath = useRemote ? pkg.remote_manifest : pkg.local_manifest
|
||||
|
||||
if (!fs.existsSync(manifestPath)) {
|
||||
BaseLog.error(`Manifest not found in expected path [${manifestPath}]
|
||||
\nMaybe the package installation has not been completed yet or corrupted.
|
||||
`)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const ManifestRead = await ManifestReader(manifestPath)
|
||||
|
||||
const manifest = await ManifestVM(ManifestRead.code)
|
||||
|
||||
if (typeof manifest.execute === "function") {
|
||||
await manifest.execute(pkg)
|
||||
}
|
||||
|
||||
if (typeof manifest.execute === "string") {
|
||||
manifest.execute = parseStringVars(manifest.execute, pkg)
|
||||
|
||||
BaseLog.info(`Executing binary > [${manifest.execute}]`)
|
||||
|
||||
const args = Array.isArray(manifest.execute_args) ? manifest.execute_args : []
|
||||
|
||||
await execa(manifest.execute, args, {
|
||||
cwd: pkg.install_path,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
})
|
||||
}
|
||||
|
||||
return pkg
|
||||
} catch (error) {
|
||||
global._relic_eventBus.emit(`pkg:${pkg_id}:error`, error)
|
||||
|
||||
BaseLog.error(`Failed to execute package [${pkg_id}]`, error)
|
||||
BaseLog.error(error.stack)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
163
packages/core/src/handlers/install.js
Normal file
163
packages/core/src/handlers/install.js
Normal file
@ -0,0 +1,163 @@
|
||||
import fs from "node:fs"
|
||||
|
||||
import DB from "../db"
|
||||
import SetupHelper from "../helpers/setup"
|
||||
import ManifestReader from "../manifest/reader"
|
||||
import ManifestVM from "../manifest/vm"
|
||||
import GenericSteps from "../generic_steps"
|
||||
import Apply from "../handlers/apply"
|
||||
|
||||
const BaseLog = Logger.child({ service: "INSTALLER" })
|
||||
|
||||
export default async function install(manifest) {
|
||||
let id = null
|
||||
|
||||
try {
|
||||
await SetupHelper()
|
||||
|
||||
BaseLog.info(`Invoking new installation...`)
|
||||
BaseLog.info(`Fetching manifest [${manifest}]`)
|
||||
|
||||
const ManifestRead = await ManifestReader(manifest)
|
||||
|
||||
manifest = await ManifestVM(ManifestRead.code)
|
||||
|
||||
id = manifest.constructor.id
|
||||
|
||||
const Log = BaseLog.child({ service: `INSTALLER|${id}` })
|
||||
|
||||
Log.info(`Creating install path [${manifest.install_path}]`)
|
||||
|
||||
if (fs.existsSync(manifest.install_path)) {
|
||||
Log.info(`Package already exists, removing...`)
|
||||
await fs.rmSync(manifest.install_path, { recursive: true })
|
||||
}
|
||||
|
||||
await fs.mkdirSync(manifest.install_path, { recursive: true })
|
||||
|
||||
Log.info(`Initializing manifest...`)
|
||||
|
||||
if (typeof manifest.initialize === "function") {
|
||||
await manifest.initialize()
|
||||
}
|
||||
|
||||
Log.info(`Appending to db...`)
|
||||
|
||||
const pkg = DB.defaultPackageState({
|
||||
id: id,
|
||||
name: manifest.constructor.pkg_name,
|
||||
version: manifest.constructor.version,
|
||||
install_path: manifest.install_path,
|
||||
description: manifest.constructor.description,
|
||||
license: manifest.constructor.license,
|
||||
last_status: "installing",
|
||||
remote_manifest: ManifestRead.remote_manifest,
|
||||
local_manifest: ManifestRead.local_manifest,
|
||||
})
|
||||
|
||||
await DB.writePackage(pkg)
|
||||
|
||||
global._relic_eventBus.emit("pkg:new", pkg)
|
||||
|
||||
if (manifest.configuration) {
|
||||
Log.info(`Applying default config to package...`)
|
||||
|
||||
pkg.config = Object.entries(manifest.configuration).reduce((acc, [key, value]) => {
|
||||
acc[key] = value.default
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
if (typeof manifest.beforeInstall === "function") {
|
||||
Log.info(`Executing beforeInstall hook...`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${id}`, {
|
||||
status_text: `Performing beforeInstall hook...`,
|
||||
})
|
||||
|
||||
await manifest.beforeInstall(pkg)
|
||||
}
|
||||
|
||||
if (Array.isArray(manifest.installSteps)) {
|
||||
Log.info(`Executing generic install steps...`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${id}`, {
|
||||
status_text: `Performing generic install steps...`,
|
||||
})
|
||||
|
||||
await GenericSteps(pkg, manifest.installSteps, Log)
|
||||
}
|
||||
|
||||
if (typeof manifest.afterInstall === "function") {
|
||||
Log.info(`Executing afterInstall hook...`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${id}`, {
|
||||
status_text: `Performing afterInstall hook...`,
|
||||
})
|
||||
|
||||
await manifest.afterInstall(pkg)
|
||||
}
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${id}`, {
|
||||
status_text: `Finishing up...`,
|
||||
})
|
||||
|
||||
Log.info(`Copying manifest to the final location...`)
|
||||
|
||||
const finalPath = `${manifest.install_path}/.rmanifest`
|
||||
|
||||
if (fs.existsSync(finalPath)) {
|
||||
await fs.promises.unlink(finalPath)
|
||||
}
|
||||
|
||||
await fs.promises.copyFile(ManifestRead.local_manifest, finalPath)
|
||||
|
||||
if (ManifestRead.is_catched) {
|
||||
Log.info(`Removing cache manifest...`)
|
||||
await fs.promises.unlink(ManifestRead.local_manifest)
|
||||
}
|
||||
|
||||
pkg.local_manifest = finalPath
|
||||
pkg.last_status = "installed"
|
||||
pkg.installed_at = Date.now()
|
||||
|
||||
await DB.writePackage(pkg)
|
||||
|
||||
if (manifest.patches) {
|
||||
const defaultPatches = manifest.patches.filter((patch) => patch.default)
|
||||
|
||||
if (defaultPatches.length > 0) {
|
||||
Log.info(`Applying default patches...`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${id}`, {
|
||||
status_text: `Applying default patches...`,
|
||||
})
|
||||
|
||||
await Apply(id, {
|
||||
patches: Object.fromEntries(defaultPatches.map((patch) => [patch.id, true])),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${id}`, {
|
||||
status_text: `Installation completed successfully`,
|
||||
})
|
||||
|
||||
Log.info(`Package installed successfully!`)
|
||||
|
||||
return pkg
|
||||
} catch (error) {
|
||||
global._relic_eventBus.emit(`pkg:${id}:error`, error)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${id}`, {
|
||||
last_status: "failed",
|
||||
status_text: `Installation failed`,
|
||||
})
|
||||
|
||||
BaseLog.error(`Error during installation of package [${id}] >`, error)
|
||||
BaseLog.error(error.stack)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
5
packages/core/src/handlers/list.js
Normal file
5
packages/core/src/handlers/list.js
Normal file
@ -0,0 +1,5 @@
|
||||
import DB from "../db"
|
||||
|
||||
export default async function list() {
|
||||
return await DB.getPackages()
|
||||
}
|
63
packages/core/src/handlers/uninstall.js
Normal file
63
packages/core/src/handlers/uninstall.js
Normal file
@ -0,0 +1,63 @@
|
||||
import DB from "../db"
|
||||
import ManifestReader from "../manifest/reader"
|
||||
import ManifestVM from "../manifest/vm"
|
||||
|
||||
import { rimraf } from "rimraf"
|
||||
|
||||
const BaseLog = Logger.child({ service: "UNINSTALLER" })
|
||||
|
||||
export default async function uninstall(pkg_id) {
|
||||
try {
|
||||
const pkg = await DB.getPackages(pkg_id)
|
||||
|
||||
if (!pkg) {
|
||||
BaseLog.info(`Package not found [${pkg_id}]`)
|
||||
return null
|
||||
}
|
||||
|
||||
const Log = Logger.child({ service: `UNINSTALLER|${pkg.id}` })
|
||||
|
||||
Log.info(`Uninstalling package...`)
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Uninstalling package...`,
|
||||
})
|
||||
|
||||
const ManifestRead = await ManifestReader(pkg.local_manifest)
|
||||
const manifest = await ManifestVM(ManifestRead.code)
|
||||
|
||||
if (typeof manifest.uninstall === "function") {
|
||||
Log.info(`Performing uninstall hook...`)
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Performing uninstall hook...`,
|
||||
})
|
||||
await manifest.uninstall(pkg)
|
||||
}
|
||||
|
||||
Log.info(`Deleting package directory...`)
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Deleting package directory...`,
|
||||
})
|
||||
await rimraf(pkg.install_path)
|
||||
|
||||
Log.info(`Removing package from database...`)
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Removing package from database...`,
|
||||
})
|
||||
await DB.deletePackage(pkg.id)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status: "deleted",
|
||||
status_text: `Uninstalling package...`,
|
||||
})
|
||||
Log.info(`Package uninstalled successfully!`)
|
||||
|
||||
return pkg
|
||||
} catch (error) {
|
||||
global._relic_eventBus.emit(`pkg:${pkg_id}:error`, error)
|
||||
|
||||
BaseLog.error(`Failed to uninstall package [${pkg_id}]`, error)
|
||||
BaseLog.error(error.stack)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
114
packages/core/src/handlers/update.js
Normal file
114
packages/core/src/handlers/update.js
Normal file
@ -0,0 +1,114 @@
|
||||
import DB from "../db"
|
||||
|
||||
import ManifestReader from "../manifest/reader"
|
||||
import ManifestVM from "../manifest/vm"
|
||||
|
||||
import GenericSteps from "../generic_steps"
|
||||
import PatchManager from "../classes/PatchManager"
|
||||
|
||||
const BaseLog = Logger.child({ service: "UPDATER" })
|
||||
|
||||
const AllowedPkgChanges = [
|
||||
"id",
|
||||
"name",
|
||||
"version",
|
||||
"description",
|
||||
"author",
|
||||
"license",
|
||||
"icon",
|
||||
"core_minimum_version",
|
||||
"remote_manifest",
|
||||
]
|
||||
|
||||
const ManifestKeysMap = {
|
||||
"name": "pkg_name",
|
||||
}
|
||||
|
||||
export default async function update(pkg_id) {
|
||||
try {
|
||||
const pkg = await DB.getPackages(pkg_id)
|
||||
|
||||
if (!pkg) {
|
||||
BaseLog.error(`Package not found [${pkg_id}]`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const Log = BaseLog.child({ service: `UPDATER|${pkg.id}` })
|
||||
|
||||
let ManifestRead = await ManifestReader(pkg.local_manifest)
|
||||
let manifest = await ManifestVM(ManifestRead.code)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status: "updating",
|
||||
status_text: `Updating package...`,
|
||||
})
|
||||
|
||||
if (typeof manifest.update === "function") {
|
||||
Log.info(`Performing update hook...`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Performing update hook...`,
|
||||
})
|
||||
|
||||
await manifest.update(pkg)
|
||||
}
|
||||
|
||||
if (manifest.updateSteps) {
|
||||
Log.info(`Performing update steps...`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Performing update steps...`,
|
||||
})
|
||||
|
||||
await GenericSteps(pkg, manifest.updateSteps, Log)
|
||||
}
|
||||
|
||||
if (Array.isArray(pkg.applied_patches)) {
|
||||
const patchManager = new PatchManager(pkg, manifest)
|
||||
|
||||
await patchManager.patch(pkg.applied_patches)
|
||||
}
|
||||
|
||||
if (typeof manifest.afterUpdate === "function") {
|
||||
Log.info(`Performing after update hook...`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status_text: `Performing after update hook...`,
|
||||
})
|
||||
|
||||
await manifest.afterUpdate(pkg)
|
||||
}
|
||||
|
||||
ManifestRead = await ManifestReader(pkg.local_manifest)
|
||||
manifest = await ManifestVM(ManifestRead.code)
|
||||
|
||||
// override public static values
|
||||
for await (const key of AllowedPkgChanges) {
|
||||
if (key in manifest.constructor) {
|
||||
const mapKey = ManifestKeysMap[key] || key
|
||||
pkg[key] = manifest.constructor[mapKey]
|
||||
}
|
||||
}
|
||||
|
||||
pkg.status = "installed"
|
||||
pkg.last_update = Date.now()
|
||||
|
||||
await DB.writePackage(pkg)
|
||||
|
||||
Log.info(`Package updated successfully`)
|
||||
|
||||
global._relic_eventBus.emit(`pkg:update:state:${pkg.id}`, {
|
||||
status: "installed",
|
||||
})
|
||||
|
||||
return pkg
|
||||
} catch (error) {
|
||||
global._relic_eventBus.emit(`pkg:${pkg_id}:error`, error)
|
||||
|
||||
BaseLog.error(`Failed to update package [${pkg_id}]`, error)
|
||||
BaseLog.error(error.stack)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
73
packages/core/src/helpers/downloadHttpFile.js
Normal file
73
packages/core/src/helpers/downloadHttpFile.js
Normal file
@ -0,0 +1,73 @@
|
||||
import fs from "node:fs"
|
||||
import axios from "axios"
|
||||
import humanFormat from "human-format"
|
||||
import cliProgress from "cli-progress"
|
||||
|
||||
function convertSize(size) {
|
||||
return `${humanFormat(size, {
|
||||
decimals: 2,
|
||||
})}B`
|
||||
}
|
||||
|
||||
export default async (url, destination, progressCallback) => {
|
||||
const progressBar = new cliProgress.SingleBar({
|
||||
format: "[{bar}] {percentage}% | {total_formatted} | {speed}/s | {eta_formatted}",
|
||||
barCompleteChar: "\u2588",
|
||||
barIncompleteChar: "\u2591",
|
||||
hideCursor: true
|
||||
}, cliProgress.Presets.shades_classic)
|
||||
|
||||
const { data: remoteStream, headers } = await axios.get(url, {
|
||||
responseType: "stream",
|
||||
})
|
||||
|
||||
const localStream = fs.createWriteStream(destination)
|
||||
|
||||
let progress = {
|
||||
total: Number(headers["content-length"] ?? 0),
|
||||
transferred: 0,
|
||||
speed: 0,
|
||||
}
|
||||
|
||||
let lastTickTransferred = 0
|
||||
|
||||
progressBar.start(progress.total, 0, {
|
||||
speed: "0B/s",
|
||||
total_formatted: convertSize(progress.total),
|
||||
})
|
||||
|
||||
remoteStream.pipe(localStream)
|
||||
|
||||
remoteStream.on("data", (data) => {
|
||||
progress.transferred = progress.transferred + Buffer.byteLength(data)
|
||||
})
|
||||
|
||||
const progressInterval = setInterval(() => {
|
||||
progress.speed = ((progress.transferred ?? 0) - lastTickTransferred) / 1
|
||||
|
||||
lastTickTransferred = progress.transferred ?? 0
|
||||
|
||||
progress.transferredString = convertSize(progress.transferred ?? 0)
|
||||
progress.totalString = convertSize(progress.total)
|
||||
progress.speedString = convertSize(progress.speed)
|
||||
|
||||
progressBar.update(progress.transferred, {
|
||||
speed: progress.speedString,
|
||||
})
|
||||
|
||||
if (typeof progressCallback === "function") {
|
||||
progressCallback(progress)
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
localStream.on("finish", resolve)
|
||||
localStream.on("error", reject)
|
||||
})
|
||||
|
||||
progressBar.stop()
|
||||
|
||||
clearInterval(progressInterval)
|
||||
|
||||
return destination
|
||||
}
|
43
packages/core/src/helpers/sendToRender.js
Normal file
43
packages/core/src/helpers/sendToRender.js
Normal file
@ -0,0 +1,43 @@
|
||||
import lodash from "lodash"
|
||||
|
||||
const forbidden = [
|
||||
"libraries"
|
||||
]
|
||||
|
||||
export default (event, data) => {
|
||||
if (!global.win) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
function serializeIpc(data) {
|
||||
if (!data) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
data = JSON.stringify(data)
|
||||
|
||||
data = JSON.parse(data)
|
||||
|
||||
const copy = lodash.cloneDeep(data)
|
||||
|
||||
if (!Array.isArray(copy)) {
|
||||
Object.keys(copy).forEach((key) => {
|
||||
if (forbidden.includes(key)) {
|
||||
delete copy[key]
|
||||
}
|
||||
|
||||
if (typeof copy[key] === "function") {
|
||||
delete copy[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return copy
|
||||
}
|
||||
|
||||
global.win.webContents.send(event, serializeIpc(data))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
125
packages/core/src/helpers/setup.js
Normal file
125
packages/core/src/helpers/setup.js
Normal file
@ -0,0 +1,125 @@
|
||||
import Logger from "../logger"
|
||||
|
||||
const Log = Logger.child({ service: "SETUP" })
|
||||
|
||||
import path from "node:path"
|
||||
import fs from "node:fs"
|
||||
import os from "node:os"
|
||||
import admzip from "adm-zip"
|
||||
import resolveOs from "../utils/resolveOs"
|
||||
import chmodRecursive from "../utils/chmodRecursive"
|
||||
|
||||
import downloadFile from "../helpers/downloadHttpFile"
|
||||
|
||||
import Vars from "../vars"
|
||||
import Prerequisites from "../prerequisites"
|
||||
|
||||
export default async () => {
|
||||
if (!fs.existsSync(Vars.binaries_path)) {
|
||||
Log.info(`Creating binaries directory: ${Vars.binaries_path}...`)
|
||||
await fs.promises.mkdir(Vars.binaries_path, { recursive: true })
|
||||
}
|
||||
|
||||
for await (let prerequisite of Prerequisites) {
|
||||
try {
|
||||
Log.info(`Checking prerequisite: ${prerequisite.id}...`)
|
||||
|
||||
if (Array.isArray(prerequisite.requireOs) && !prerequisite.requireOs.includes(os.platform())) {
|
||||
Log.info(`Prerequisite: ${prerequisite.id} is not required for this os.`)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!fs.existsSync(prerequisite.finalBin)) {
|
||||
Log.info(`Missing prerequisite: ${prerequisite.id}, installing...`)
|
||||
|
||||
if (fs.existsSync(prerequisite.destination)) {
|
||||
Log.info(`Deleting temporal file [${prerequisite.destination}]`)
|
||||
await fs.promises.rm(prerequisite.destination)
|
||||
}
|
||||
|
||||
if (fs.existsSync(prerequisite.extract)) {
|
||||
Log.info(`Deleting temporal directory [${prerequisite.extract}]`)
|
||||
await fs.promises.rm(prerequisite.extract, { recursive: true })
|
||||
}
|
||||
|
||||
Log.info(`Creating base directory: ${Vars.binaries_path}/${prerequisite.id}...`)
|
||||
await fs.promises.mkdir(path.resolve(Vars.binaries_path, prerequisite.id), { recursive: true })
|
||||
|
||||
if (typeof prerequisite.url === "function") {
|
||||
prerequisite.url = await prerequisite.url(resolveOs(), os.arch())
|
||||
Log.info(`Resolved url: ${prerequisite.url}`)
|
||||
}
|
||||
|
||||
Log.info(`Downloading ${prerequisite.id} from [${prerequisite.url}] to destination [${prerequisite.destination}]...`)
|
||||
|
||||
try {
|
||||
await downloadFile(
|
||||
prerequisite.url,
|
||||
prerequisite.destination
|
||||
)
|
||||
} catch (error) {
|
||||
await fs.promises.rm(prerequisite.destination)
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
if (typeof prerequisite.extract === "string") {
|
||||
Log.info(`Extracting ${prerequisite.id} to destination [${prerequisite.extract}]...`)
|
||||
|
||||
const zip = new admzip(prerequisite.destination)
|
||||
|
||||
await zip.extractAllTo(prerequisite.extract, true)
|
||||
|
||||
Log.info(`Extraction ok...`)
|
||||
}
|
||||
|
||||
if (prerequisite.extractTargetFromName === true) {
|
||||
let name = path.basename(prerequisite.url)
|
||||
const ext = path.extname(name)
|
||||
|
||||
name = name.replace(ext, "")
|
||||
|
||||
if (fs.existsSync(path.resolve(prerequisite.extract, name))) {
|
||||
await fs.promises.rename(path.resolve(prerequisite.extract, name), `${prerequisite.extract}_old`)
|
||||
await fs.promises.rm(prerequisite.extract, { recursive: true })
|
||||
await fs.promises.rename(`${prerequisite.extract}_old`, prerequisite.extract)
|
||||
}
|
||||
}
|
||||
|
||||
if (prerequisite.deleteBeforeExtract === true) {
|
||||
Log.info(`Deleting temporal file [${prerequisite.destination}]`)
|
||||
await fs.promises.unlink(prerequisite.destination)
|
||||
}
|
||||
|
||||
if (typeof prerequisite.rewriteExecutionPermission !== "undefined") {
|
||||
const to = typeof prerequisite.rewriteExecutionPermission === "string" ?
|
||||
prerequisite.rewriteExecutionPermission :
|
||||
prerequisite.finalBin
|
||||
|
||||
Log.info(`Rewriting permissions to ${to}...`)
|
||||
await chmodRecursive(to, 0o755)
|
||||
}
|
||||
|
||||
if (Array.isArray(prerequisite.moveDirs)) {
|
||||
for (const dir of prerequisite.moveDirs) {
|
||||
Log.info(`Moving ${dir.from} to ${dir.to}...`)
|
||||
|
||||
await fs.promises.rename(dir.from, dir.to)
|
||||
|
||||
if (dir.deleteParentBefore === true) {
|
||||
await fs.promises.rm(path.dirname(dir.from), { recursive: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.info(`Prerequisite: ${prerequisite.id} is ready!`)
|
||||
} catch (error) {
|
||||
Log.error(error)
|
||||
Log.error("Aborting setup due to an error...")
|
||||
return false
|
||||
}
|
||||
|
||||
Log.info(`All prerequisites are ready!`)
|
||||
}
|
||||
}
|
52
packages/core/src/index.js
Normal file
52
packages/core/src/index.js
Normal file
@ -0,0 +1,52 @@
|
||||
import fs from "node:fs"
|
||||
import { EventEmitter } from "@foxify/events"
|
||||
import { onExit } from "signal-exit"
|
||||
import open from "open"
|
||||
|
||||
import SetupHelper from "./helpers/setup"
|
||||
import Logger from "./logger"
|
||||
|
||||
import Vars from "./vars"
|
||||
import DB from "./db"
|
||||
|
||||
export default class RelicCore {
|
||||
constructor(params) {
|
||||
this.params = params
|
||||
}
|
||||
|
||||
eventBus = global._relic_eventBus = new EventEmitter()
|
||||
logger = global.Logger = Logger
|
||||
|
||||
async initialize() {
|
||||
await DB.initialize()
|
||||
|
||||
onExit(this.onExit)
|
||||
}
|
||||
|
||||
onExit = () => {
|
||||
if (fs.existsSync(Vars.cache_path)) {
|
||||
fs.rmSync(Vars.cache_path, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
|
||||
async setup() {
|
||||
return await SetupHelper()
|
||||
}
|
||||
|
||||
package = {
|
||||
install: require("./handlers/install").default,
|
||||
execute: require("./handlers/execute").default,
|
||||
uninstall: require("./handlers/uninstall").default,
|
||||
update: require("./handlers/update").default,
|
||||
apply: require("./handlers/apply").default,
|
||||
list: require("./handlers/list").default,
|
||||
}
|
||||
|
||||
openPath(pkg_id) {
|
||||
if (!pkg_id) {
|
||||
return open(Vars.runtime_path)
|
||||
}
|
||||
|
||||
return open(Vars.packages_path + "/" + pkg_id)
|
||||
}
|
||||
}
|
24
packages/core/src/libraries/lowdb/adapters/Memory.js
Normal file
24
packages/core/src/libraries/lowdb/adapters/Memory.js
Normal file
@ -0,0 +1,24 @@
|
||||
export class Memory {
|
||||
#data = null
|
||||
|
||||
read() {
|
||||
return Promise.resolve(this.#data)
|
||||
}
|
||||
|
||||
write(obj) {
|
||||
this.#data = obj
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
export class MemorySync {
|
||||
#data = null
|
||||
|
||||
read() {
|
||||
return this.#data || null
|
||||
}
|
||||
|
||||
write(obj) {
|
||||
this.#data = obj
|
||||
}
|
||||
}
|
51
packages/core/src/libraries/lowdb/adapters/node/DataFile.js
Normal file
51
packages/core/src/libraries/lowdb/adapters/node/DataFile.js
Normal file
@ -0,0 +1,51 @@
|
||||
import { TextFile, TextFileSync } from "./TextFile.js"
|
||||
|
||||
export class DataFile {
|
||||
#adapter
|
||||
#parse
|
||||
#stringify
|
||||
|
||||
constructor(filename, { parse, stringify }) {
|
||||
this.#adapter = new TextFile(filename)
|
||||
this.#parse = parse
|
||||
this.#stringify = stringify
|
||||
}
|
||||
|
||||
async read() {
|
||||
const data = await this.#adapter.read()
|
||||
if (data === null) {
|
||||
return null
|
||||
} else {
|
||||
return this.#parse(data)
|
||||
}
|
||||
}
|
||||
|
||||
write(obj) {
|
||||
return this.#adapter.write(this.#stringify(obj))
|
||||
}
|
||||
}
|
||||
|
||||
export class DataFileSync {
|
||||
#adapter
|
||||
#parse
|
||||
#stringify
|
||||
|
||||
constructor(filename, { parse, stringify }) {
|
||||
this.#adapter = new TextFileSync(filename)
|
||||
this.#parse = parse
|
||||
this.#stringify = stringify
|
||||
}
|
||||
|
||||
read() {
|
||||
const data = this.#adapter.read()
|
||||
if (data === null) {
|
||||
return null
|
||||
} else {
|
||||
return this.#parse(data)
|
||||
}
|
||||
}
|
||||
|
||||
write(obj) {
|
||||
this.#adapter.write(this.#stringify(obj))
|
||||
}
|
||||
}
|
19
packages/core/src/libraries/lowdb/adapters/node/JSONFile.js
Normal file
19
packages/core/src/libraries/lowdb/adapters/node/JSONFile.js
Normal file
@ -0,0 +1,19 @@
|
||||
import { DataFile, DataFileSync } from "./DataFile.js";
|
||||
|
||||
export class JSONFile extends DataFile {
|
||||
constructor(filename) {
|
||||
super(filename, {
|
||||
parse: JSON.parse,
|
||||
stringify: (data) => JSON.stringify(data, null, 2),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class JSONFileSync extends DataFileSync {
|
||||
constructor(filename) {
|
||||
super(filename, {
|
||||
parse: JSON.parse,
|
||||
stringify: (data) => JSON.stringify(data, null, 2),
|
||||
});
|
||||
}
|
||||
}
|
65
packages/core/src/libraries/lowdb/adapters/node/TextFile.js
Normal file
65
packages/core/src/libraries/lowdb/adapters/node/TextFile.js
Normal file
@ -0,0 +1,65 @@
|
||||
import { readFileSync, renameSync, writeFileSync } from "node:fs"
|
||||
import { readFile } from "node:fs/promises"
|
||||
import path from "node:path"
|
||||
|
||||
import { Writer } from "../../steno"
|
||||
|
||||
export class TextFile {
|
||||
#filename
|
||||
#writer
|
||||
|
||||
constructor(filename) {
|
||||
this.#filename = filename
|
||||
this.#writer = new Writer(filename)
|
||||
}
|
||||
|
||||
async read() {
|
||||
let data
|
||||
|
||||
try {
|
||||
data = await readFile(this.#filename, "utf-8")
|
||||
} catch (e) {
|
||||
if (e.code === "ENOENT") {
|
||||
return null
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
write(str) {
|
||||
return this.#writer.write(str)
|
||||
}
|
||||
}
|
||||
|
||||
export class TextFileSync {
|
||||
#tempFilename
|
||||
#filename
|
||||
|
||||
constructor(filename) {
|
||||
this.#filename = filename
|
||||
const f = filename.toString()
|
||||
this.#tempFilename = path.join(path.dirname(f), `.${path.basename(f)}.tmp`)
|
||||
}
|
||||
|
||||
read() {
|
||||
let data
|
||||
|
||||
try {
|
||||
data = readFileSync(this.#filename, "utf-8")
|
||||
} catch (e) {
|
||||
if (e.code === "ENOENT") {
|
||||
return null
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
write(str) {
|
||||
writeFileSync(this.#tempFilename, str)
|
||||
renameSync(this.#tempFilename, this.#filename)
|
||||
}
|
||||
}
|
48
packages/core/src/libraries/lowdb/core/Low.js
Normal file
48
packages/core/src/libraries/lowdb/core/Low.js
Normal file
@ -0,0 +1,48 @@
|
||||
function checkArgs(adapter, defaultData) {
|
||||
if (adapter === undefined) throw new Error("lowdb: missing adapter")
|
||||
if (defaultData === undefined) throw new Error("lowdb: missing default data")
|
||||
}
|
||||
|
||||
export class Low {
|
||||
constructor(adapter, defaultData) {
|
||||
checkArgs(adapter, defaultData)
|
||||
this.adapter = adapter
|
||||
this.data = defaultData
|
||||
}
|
||||
|
||||
async read() {
|
||||
const data = await this.adapter.read()
|
||||
if (data) this.data = data
|
||||
}
|
||||
|
||||
async write() {
|
||||
if (this.data) await this.adapter.write(this.data)
|
||||
}
|
||||
|
||||
async update(fn) {
|
||||
fn(this.data)
|
||||
await this.write()
|
||||
}
|
||||
}
|
||||
|
||||
export class LowSync {
|
||||
constructor(adapter, defaultData) {
|
||||
checkArgs(adapter, defaultData)
|
||||
this.adapter = adapter
|
||||
this.data = defaultData
|
||||
}
|
||||
|
||||
read() {
|
||||
const data = this.adapter.read()
|
||||
if (data) this.data = data
|
||||
}
|
||||
|
||||
write() {
|
||||
if (this.data) this.adapter.write(this.data)
|
||||
}
|
||||
|
||||
update(fn) {
|
||||
fn(this.data)
|
||||
this.write()
|
||||
}
|
||||
}
|
23
packages/core/src/libraries/lowdb/presets/node.js
Normal file
23
packages/core/src/libraries/lowdb/presets/node.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { Memory, MemorySync } from "../adapters/Memory.js"
|
||||
import { JSONFile, JSONFileSync } from "../adapters/node/JSONFile.js"
|
||||
import { Low, LowSync } from "../core/Low.js"
|
||||
|
||||
export async function JSONFilePreset(filename, defaultData) {
|
||||
const adapter = process.env.NODE_ENV === "test" ? new Memory() : new JSONFile(filename)
|
||||
|
||||
const db = new Low(adapter, defaultData)
|
||||
|
||||
await db.read()
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
export function JSONFileSyncPreset(filename, defaultData) {
|
||||
const adapter = process.env.NODE_ENV === "test" ? new MemorySync() : new JSONFileSync(filename)
|
||||
|
||||
const db = new LowSync(adapter, defaultData)
|
||||
|
||||
db.read()
|
||||
|
||||
return db
|
||||
}
|
98
packages/core/src/libraries/lowdb/steno/index.js
Normal file
98
packages/core/src/libraries/lowdb/steno/index.js
Normal file
@ -0,0 +1,98 @@
|
||||
import { rename, writeFile } from "node:fs/promises"
|
||||
import { basename, dirname, join } from "node:path"
|
||||
import { fileURLToPath } from "node:url"
|
||||
|
||||
// Returns a temporary file
|
||||
// Example: for /some/file will return /some/.file.tmp
|
||||
function getTempFilename(file) {
|
||||
const f = file instanceof URL ? fileURLToPath(file) : file.toString()
|
||||
return join(dirname(f), `.${basename(f)}.tmp`)
|
||||
}
|
||||
|
||||
// Retries an asynchronous operation with a delay between retries and a maximum retry count
|
||||
async function retryAsyncOperation(fn, maxRetries, delayMs) {
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
return await fn()
|
||||
} catch (error) {
|
||||
if (i < maxRetries - 1) {
|
||||
await new Promise(resolve => setTimeout(resolve, delayMs))
|
||||
} else {
|
||||
throw error // Rethrow the error if max retries reached
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Writer {
|
||||
#filename
|
||||
#tempFilename
|
||||
#locked = false
|
||||
#prev = null
|
||||
#next = null
|
||||
#nextPromise = null
|
||||
#nextData = null
|
||||
|
||||
// File is locked, add data for later
|
||||
#add(data) {
|
||||
// Only keep most recent data
|
||||
this.#nextData = data
|
||||
|
||||
// Create a singleton promise to resolve all next promises once next data is written
|
||||
this.#nextPromise ||= new Promise((resolve, reject) => {
|
||||
this.#next = [resolve, reject]
|
||||
})
|
||||
|
||||
// Return a promise that will resolve at the same time as next promise
|
||||
return new Promise((resolve, reject) => {
|
||||
this.#nextPromise?.then(resolve).catch(reject)
|
||||
})
|
||||
}
|
||||
|
||||
// File isn't locked, write data
|
||||
async #write(data) {
|
||||
// Lock file
|
||||
this.#locked = true
|
||||
try {
|
||||
// Atomic write
|
||||
await writeFile(this.#tempFilename, data, "utf-8")
|
||||
await retryAsyncOperation(
|
||||
async () => {
|
||||
await rename(this.#tempFilename, this.#filename)
|
||||
},
|
||||
10,
|
||||
100
|
||||
)
|
||||
|
||||
// Call resolve
|
||||
this.#prev?.[0]()
|
||||
} catch (err) {
|
||||
// Call reject
|
||||
if (err instanceof Error) {
|
||||
this.#prev?.[1](err)
|
||||
}
|
||||
throw err
|
||||
} finally {
|
||||
// Unlock file
|
||||
this.#locked = false
|
||||
|
||||
this.#prev = this.#next
|
||||
this.#next = this.#nextPromise = null
|
||||
|
||||
if (this.#nextData !== null) {
|
||||
const nextData = this.#nextData
|
||||
this.#nextData = null
|
||||
await this.write(nextData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(filename) {
|
||||
this.#filename = filename
|
||||
this.#tempFilename = getTempFilename(filename)
|
||||
}
|
||||
|
||||
async write(data) {
|
||||
return this.#locked ? this.#add(data) : this.#write(data)
|
||||
}
|
||||
}
|
40
packages/core/src/logger.js
Normal file
40
packages/core/src/logger.js
Normal file
@ -0,0 +1,40 @@
|
||||
import winston from "winston"
|
||||
import colors from "cli-color"
|
||||
|
||||
const servicesToColor = {
|
||||
"CORE": {
|
||||
color: "whiteBright",
|
||||
background: "bgBlackBright",
|
||||
},
|
||||
"INSTALL": {
|
||||
color: "whiteBright",
|
||||
background: "bgBlueBright",
|
||||
},
|
||||
}
|
||||
|
||||
const paintText = (level, service, ...args) => {
|
||||
let { color, background } = servicesToColor[service ?? "CORE"] ?? servicesToColor["CORE"]
|
||||
|
||||
if (level === "error") {
|
||||
color = "whiteBright"
|
||||
background = "bgRedBright"
|
||||
}
|
||||
|
||||
return colors[background][color](...args)
|
||||
}
|
||||
|
||||
const format = winston.format.printf(({ timestamp, service = "CORE", level, message, }) => {
|
||||
return `${paintText(level, service, `(${level}) [${service}]`)} > ${message}`
|
||||
})
|
||||
|
||||
export default winston.createLogger({
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
format
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
//new winston.transports.File({ filename: "error.log", level: "error" }),
|
||||
//new winston.transports.File({ filename: "combined.log" }),
|
||||
],
|
||||
})
|
23
packages/core/src/manifest/libraries.js
Normal file
23
packages/core/src/manifest/libraries.js
Normal file
@ -0,0 +1,23 @@
|
||||
import PublicInternalLibraries from "./libs"
|
||||
|
||||
const isAClass = (x) => x && typeof x === "function" && x.prototype && typeof x.prototype.constructor === "function"
|
||||
|
||||
export default async (dependencies, bindCtx) => {
|
||||
const libraries = {}
|
||||
|
||||
for await (const lib of dependencies) {
|
||||
if (PublicInternalLibraries[lib]) {
|
||||
if (typeof PublicInternalLibraries[lib] === "function" && isAClass(PublicInternalLibraries[lib])) {
|
||||
libraries[lib] = new PublicInternalLibraries[lib](bindCtx)
|
||||
|
||||
if (libraries[lib].initialize) {
|
||||
await libraries[lib].initialize()
|
||||
}
|
||||
} else {
|
||||
libraries[lib] = PublicInternalLibraries[lib]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return libraries
|
||||
}
|
61
packages/core/src/manifest/libs/auth/index.js
Normal file
61
packages/core/src/manifest/libs/auth/index.js
Normal file
@ -0,0 +1,61 @@
|
||||
import open from "open"
|
||||
import axios from "axios"
|
||||
|
||||
export default class Auth {
|
||||
constructor(ctx) {
|
||||
this.manifest = ctx.manifest
|
||||
}
|
||||
|
||||
async get() {
|
||||
return {
|
||||
assigned_username: "test",
|
||||
}
|
||||
|
||||
const authData = global.authService.getAuth(this.manifest.id)
|
||||
|
||||
console.log(authData)
|
||||
|
||||
if (authData && this.manifest.auth && this.manifest.auth.getter) {
|
||||
const result = await axios({
|
||||
method: "POST",
|
||||
url: this.manifest.auth.getter,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
data: {
|
||||
auth_data: authData,
|
||||
}
|
||||
}).catch((err) => {
|
||||
sendToRender(`new:notification`, {
|
||||
type: "error",
|
||||
message: "Failed to authorize",
|
||||
description: err.response.data.message ?? err.response.data.error ?? err.message,
|
||||
duration: 10
|
||||
})
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
if (result instanceof Error) {
|
||||
throw result
|
||||
}
|
||||
|
||||
console.log(result.data)
|
||||
|
||||
return result.data
|
||||
}
|
||||
|
||||
return authData
|
||||
}
|
||||
|
||||
request() {
|
||||
return true
|
||||
if (!this.manifest.auth) {
|
||||
return false
|
||||
}
|
||||
|
||||
const authURL = this.manifest.auth.fetcher
|
||||
|
||||
open(authURL)
|
||||
}
|
||||
}
|
39
packages/core/src/manifest/libs/fs/index.js
Normal file
39
packages/core/src/manifest/libs/fs/index.js
Normal file
@ -0,0 +1,39 @@
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
// Protect from reading or write operations outside of the package directory
|
||||
export default class SecureFileSystem {
|
||||
constructor(ctx) {
|
||||
this.jailPath = ctx.manifest.install_path
|
||||
}
|
||||
|
||||
checkOutsideJail(target) {
|
||||
// if (!path.resolve(target).startsWith(this.jailPath)) {
|
||||
// throw new Error("Cannot access resource outside of package directory")
|
||||
// }
|
||||
}
|
||||
|
||||
readFileSync(destination, options) {
|
||||
this.checkOutsideJail(destination)
|
||||
|
||||
return fs.readFileSync(finalPath, options)
|
||||
}
|
||||
|
||||
copyFileSync(from, to) {
|
||||
this.checkOutsideJail(from)
|
||||
this.checkOutsideJail(to)
|
||||
|
||||
return fs.copyFileSync(from, to)
|
||||
}
|
||||
|
||||
writeFileSync(destination, data, options) {
|
||||
this.checkOutsideJail(destination)
|
||||
|
||||
return fs.writeFileSync(finalPath, data, options)
|
||||
}
|
||||
|
||||
// don't need to check finalPath
|
||||
existsSync(...args) {
|
||||
return fs.existsSync(...args)
|
||||
}
|
||||
}
|
15
packages/core/src/manifest/libs/index.js
Normal file
15
packages/core/src/manifest/libs/index.js
Normal file
@ -0,0 +1,15 @@
|
||||
import Open from "./open"
|
||||
import Path from "./path"
|
||||
import Fs from "./fs"
|
||||
import Auth from "./auth"
|
||||
|
||||
// Third party libraries
|
||||
import Mcl from "./mcl"
|
||||
|
||||
export default {
|
||||
fs: Fs,
|
||||
path: Path,
|
||||
open: Open,
|
||||
auth: Auth,
|
||||
mcl: Mcl
|
||||
}
|
47
packages/core/src/manifest/libs/mcl/index.js
Normal file
47
packages/core/src/manifest/libs/mcl/index.js
Normal file
@ -0,0 +1,47 @@
|
||||
import Client from "./launcher"
|
||||
import Authenticator from "./authenticator"
|
||||
|
||||
const Log = Logger.child({ service: "MCL" })
|
||||
|
||||
export default class MCL {
|
||||
/**
|
||||
* Asynchronously authenticate the user using the provided username and password.
|
||||
*
|
||||
* @param {string} username - the username of the user
|
||||
* @param {string} password - the password of the user
|
||||
* @return {Promise<Object>} the authentication information
|
||||
*/
|
||||
async auth(username, password) {
|
||||
return await Authenticator.getAuth(username, password)
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches a new client with the given options.
|
||||
*
|
||||
* @param {Object} opts - The options to be passed for launching the client.
|
||||
* @return {Promise<Client>} A promise that resolves with the launched client.
|
||||
*/
|
||||
async launch(opts, callbacks) {
|
||||
const launcher = new Client()
|
||||
|
||||
launcher.on("debug", (e) => console.log(e))
|
||||
launcher.on("data", (e) => console.log(e))
|
||||
launcher.on("close", (e) => console.log(e))
|
||||
launcher.on("error", (e) => console.log(e))
|
||||
|
||||
if (typeof callbacks === "undefined") {
|
||||
callbacks = {
|
||||
install: () => {
|
||||
Log.info("Downloading Minecraft assets...")
|
||||
},
|
||||
init_assets: () => {
|
||||
Log.info("Initializing Minecraft assets...")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await launcher.launch(opts, callbacks)
|
||||
|
||||
return launcher
|
||||
}
|
||||
}
|
13
packages/core/src/manifest/libs/open/index.js
Normal file
13
packages/core/src/manifest/libs/open/index.js
Normal file
@ -0,0 +1,13 @@
|
||||
import open, { apps } from "open"
|
||||
|
||||
const Log = Logger.child({ service: "OPEN-LIB" })
|
||||
|
||||
export default {
|
||||
spawn: async (...args) => {
|
||||
Log.info("Open spawned with args >")
|
||||
console.log(...args)
|
||||
|
||||
return await open(...args)
|
||||
},
|
||||
apps: apps,
|
||||
}
|
3
packages/core/src/manifest/libs/path/index.js
Normal file
3
packages/core/src/manifest/libs/path/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
import path from "node:path"
|
||||
|
||||
export default path
|
44
packages/core/src/manifest/reader.js
Normal file
44
packages/core/src/manifest/reader.js
Normal file
@ -0,0 +1,44 @@
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
import downloadHttpFile from "../helpers/downloadHttpFile"
|
||||
|
||||
import Vars from "../vars"
|
||||
|
||||
export async function readManifest(manifest) {
|
||||
// check if manifest is a directory or a url
|
||||
const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi
|
||||
|
||||
const target = manifest?.remote_url ?? manifest
|
||||
|
||||
if (urlRegex.test(target)) {
|
||||
if (!fs.existsSync(Vars.cache_path)) {
|
||||
fs.mkdirSync(Vars.cache_path, { recursive: true })
|
||||
}
|
||||
|
||||
const cachedManifest = await downloadHttpFile(manifest, path.resolve(Vars.cache_path, `${Date.now()}.rmanifest`))
|
||||
|
||||
return {
|
||||
remote_manifest: manifest,
|
||||
local_manifest: cachedManifest,
|
||||
is_catched: true,
|
||||
code: fs.readFileSync(cachedManifest, "utf8"),
|
||||
}
|
||||
} else {
|
||||
if (!fs.existsSync(target)) {
|
||||
throw new Error(`Manifest not found: ${target}`)
|
||||
}
|
||||
|
||||
if (!fs.statSync(target).isFile()) {
|
||||
throw new Error(`Manifest is not a file: ${target}`)
|
||||
}
|
||||
|
||||
return {
|
||||
remote_manifest: undefined,
|
||||
local_manifest: target,
|
||||
is_catched: false,
|
||||
code: fs.readFileSync(target, "utf8"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default readManifest
|
71
packages/core/src/manifest/vm.js
Normal file
71
packages/core/src/manifest/vm.js
Normal file
@ -0,0 +1,71 @@
|
||||
import os from "node:os"
|
||||
import vm from "node:vm"
|
||||
import path from "node:path"
|
||||
import ManifestConfigManager from "../classes/ManifestConfig"
|
||||
|
||||
import resolveOs from "../utils/resolveOs"
|
||||
import FetchLibraries from "./libraries"
|
||||
|
||||
import Vars from "../vars"
|
||||
|
||||
async function BuildManifest(baseClass, context, soft = false) {
|
||||
const configManager = new ManifestConfigManager(baseClass.id)
|
||||
|
||||
await configManager.initialize()
|
||||
|
||||
let dependencies = []
|
||||
|
||||
if (Array.isArray(baseClass.useLib)) {
|
||||
dependencies = [
|
||||
...dependencies,
|
||||
...baseClass.useLib
|
||||
]
|
||||
}
|
||||
|
||||
// inject install_path
|
||||
context.install_path = path.resolve(Vars.packages_path, baseClass.id)
|
||||
baseClass.install_path = context.install_path
|
||||
|
||||
// modify context
|
||||
context.Log = Logger.child({ service: `VM|${baseClass.id}` })
|
||||
context.Lib = await FetchLibraries(dependencies, {
|
||||
manifest: baseClass,
|
||||
install_path: context.install_path,
|
||||
})
|
||||
context.Config = configManager
|
||||
|
||||
// Construct the instance
|
||||
const instance = new baseClass()
|
||||
|
||||
instance.install_path = context.install_path
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
function injectUseManifest(code) {
|
||||
return code + "\n\nuse(Manifest);"
|
||||
}
|
||||
|
||||
export default async (code) => {
|
||||
return await new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
code = injectUseManifest(code)
|
||||
|
||||
const context = {
|
||||
Vars: Vars,
|
||||
Log: Logger.child({ service: "MANIFEST_VM" }),
|
||||
use: (baseClass) => {
|
||||
BuildManifest(baseClass, context).then(resolve)
|
||||
},
|
||||
os_string: resolveOs(),
|
||||
arch: os.arch(),
|
||||
}
|
||||
|
||||
vm.createContext(context)
|
||||
|
||||
await vm.runInContext(code, context)
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
69
packages/core/src/prerequisites.js
Normal file
69
packages/core/src/prerequisites.js
Normal file
@ -0,0 +1,69 @@
|
||||
import resolveRemoteBinPath from "./utils/resolveRemoteBinPath"
|
||||
import Vars from "./vars"
|
||||
import path from "node:path"
|
||||
import axios from "axios"
|
||||
|
||||
const baseURL = "https://storage.ragestudio.net/rstudio/binaries"
|
||||
|
||||
export default [
|
||||
{
|
||||
id: "7z-bin",
|
||||
finalBin: Vars.sevenzip_bin,
|
||||
url: resolveRemoteBinPath(`${baseURL}/7zip-bin`, process.platform === "win32" ? "7za.exe" : "7za"),
|
||||
destination: Vars.sevenzip_bin,
|
||||
rewriteExecutionPermission: true,
|
||||
},
|
||||
{
|
||||
id: "git-bin",
|
||||
finalBin: Vars.git_bin,
|
||||
url: resolveRemoteBinPath(`${baseURL}/git`, "git-bundle-2.4.0.zip"),
|
||||
destination: path.resolve(Vars.binaries_path, "git-bundle.zip"),
|
||||
extract: path.resolve(Vars.binaries_path, "git-bin"),
|
||||
requireOs: ["win32"],
|
||||
rewriteExecutionPermission: true,
|
||||
deleteBeforeExtract: true,
|
||||
},
|
||||
{
|
||||
id: "rclone-bin",
|
||||
finalBin: Vars.rclone_bin,
|
||||
url: resolveRemoteBinPath(`${baseURL}/rclone-bin`, "rclone-bin.zip"),
|
||||
destination: path.resolve(Vars.binaries_path, "rclone-bin.zip"),
|
||||
extract: path.resolve(Vars.binaries_path, "rclone-bin"),
|
||||
requireOs: ["win32"],
|
||||
rewriteExecutionPermission: true,
|
||||
deleteBeforeExtract: true,
|
||||
},
|
||||
{
|
||||
id: "java_jre_bin",
|
||||
finalBin: Vars.java_jre_bin,
|
||||
url: async (os, arch) => {
|
||||
const { data } = await axios({
|
||||
method: "GET",
|
||||
url: "https://api.azul.com/metadata/v1/zulu/packages",
|
||||
params: {
|
||||
arch: arch,
|
||||
java_version: "JAVA_22",
|
||||
os: os,
|
||||
archive_type: "zip",
|
||||
javafx_bundled: "false",
|
||||
java_package_type: "jre",
|
||||
page_size: "1",
|
||||
}
|
||||
})
|
||||
|
||||
return data[0].download_url
|
||||
},
|
||||
destination: path.resolve(Vars.binaries_path, "java-jre.zip"),
|
||||
extract: path.resolve(Vars.binaries_path, "java_jre_bin"),
|
||||
extractTargetFromName: true,
|
||||
moveDirs: [
|
||||
{
|
||||
from: path.resolve(Vars.binaries_path, "java_jre_bin", "zulu-22.jre", "Contents"),
|
||||
to: path.resolve(Vars.binaries_path, "java_jre_bin", "Contents"),
|
||||
deleteParentBefore: true
|
||||
}
|
||||
],
|
||||
rewriteExecutionPermission: path.resolve(Vars.binaries_path, "java_jre_bin"),
|
||||
deleteBeforeExtract: true,
|
||||
},
|
||||
]
|
16
packages/core/src/utils/chmodRecursive.js
Normal file
16
packages/core/src/utils/chmodRecursive.js
Normal file
@ -0,0 +1,16 @@
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
async function chmodRecursive(target, mode) {
|
||||
if (fs.lstatSync(target).isDirectory()) {
|
||||
const files = await fs.promises.readdir(target, { withFileTypes: true })
|
||||
|
||||
for (const file of files) {
|
||||
await chmodRecursive(path.join(target, file.name), mode)
|
||||
}
|
||||
} else {
|
||||
await fs.promises.chmod(target, mode)
|
||||
}
|
||||
}
|
||||
|
||||
export default chmodRecursive
|
46
packages/core/src/utils/extractFile.js
Normal file
46
packages/core/src/utils/extractFile.js
Normal file
@ -0,0 +1,46 @@
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
import { pipeline as streamPipeline } from "node:stream/promises"
|
||||
|
||||
import { extractFull } from "node-7z"
|
||||
import unzipper from "unzipper"
|
||||
|
||||
import Vars from "../vars"
|
||||
|
||||
const Log = Logger.child({ service: "EXTRACTOR" })
|
||||
|
||||
export async function extractFile(file, dest) {
|
||||
const ext = path.extname(file)
|
||||
|
||||
Log.info(`Extracting ${file} to ${dest}`)
|
||||
|
||||
switch (ext) {
|
||||
case ".zip": {
|
||||
await streamPipeline(
|
||||
fs.createReadStream(file),
|
||||
unzipper.Extract({
|
||||
path: dest,
|
||||
})
|
||||
)
|
||||
break
|
||||
}
|
||||
case ".7z": {
|
||||
await extractFull(file, dest, {
|
||||
$bin: Vars.sevenzip_bin,
|
||||
})
|
||||
break
|
||||
}
|
||||
case ".gz": {
|
||||
await extractFull(file, dest, {
|
||||
$bin: Vars.sevenzip_bin
|
||||
})
|
||||
break
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unsupported file extension: ${ext}`)
|
||||
}
|
||||
|
||||
return dest
|
||||
}
|
||||
|
||||
export default extractFile
|
21
packages/core/src/utils/parseStringVars.js
Normal file
21
packages/core/src/utils/parseStringVars.js
Normal file
@ -0,0 +1,21 @@
|
||||
export default function parseStringVars(str, pkg) {
|
||||
if (!pkg) {
|
||||
return str
|
||||
}
|
||||
|
||||
const vars = {
|
||||
id: pkg.id,
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
install_path: pkg.install_path,
|
||||
remote: pkg.remote,
|
||||
}
|
||||
|
||||
const regex = /%([^%]+)%/g
|
||||
|
||||
str = str.replace(regex, (match, varName) => {
|
||||
return vars[varName]
|
||||
})
|
||||
|
||||
return str
|
||||
}
|
17
packages/core/src/utils/resolveOs.js
Normal file
17
packages/core/src/utils/resolveOs.js
Normal file
@ -0,0 +1,17 @@
|
||||
import os from "node:os"
|
||||
|
||||
export default () => {
|
||||
if (os.platform() === "win32") {
|
||||
return "windows"
|
||||
}
|
||||
|
||||
if (os.platform() === "darwin") {
|
||||
return "macos"
|
||||
}
|
||||
|
||||
if (os.platform() === "linux") {
|
||||
return "linux"
|
||||
}
|
||||
|
||||
return os.platform()
|
||||
}
|
15
packages/core/src/utils/resolveRemoteBinPath.js
Normal file
15
packages/core/src/utils/resolveRemoteBinPath.js
Normal file
@ -0,0 +1,15 @@
|
||||
export default (pre, post) => {
|
||||
let url = null
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
url = `${pre}/mac/${process.arch}/${post}`
|
||||
}
|
||||
else if (process.platform === "win32") {
|
||||
url = `${pre}/win/${process.arch}/${post}`
|
||||
}
|
||||
else {
|
||||
url = `${pre}/linux/${process.arch}/${post}`
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
35
packages/core/src/vars.js
Normal file
35
packages/core/src/vars.js
Normal file
@ -0,0 +1,35 @@
|
||||
import path from "node:path"
|
||||
import upath from "upath"
|
||||
|
||||
const isWin = process.platform.includes("win")
|
||||
const isMac = process.platform.includes("darwin")
|
||||
|
||||
const runtimeName = "rs-relic"
|
||||
|
||||
const userdata_path = upath.normalizeSafe(path.resolve(
|
||||
process.env.APPDATA ||
|
||||
(process.platform == "darwin" ? process.env.HOME + "/Library/Preferences" : process.env.HOME + "/.local/share"),
|
||||
))
|
||||
const runtime_path = upath.normalizeSafe(path.join(userdata_path, runtimeName))
|
||||
const cache_path = upath.normalizeSafe(path.join(runtime_path, "cache"))
|
||||
const packages_path = upath.normalizeSafe(path.join(runtime_path, "packages"))
|
||||
const binaries_path = upath.normalizeSafe(path.resolve(runtime_path, "binaries"))
|
||||
const db_path = upath.normalizeSafe(path.resolve(runtime_path, "db.json"))
|
||||
|
||||
const binaries = {
|
||||
sevenzip_bin: upath.normalizeSafe(path.resolve(binaries_path, "7z-bin", isWin ? "7za.exe" : "7za")),
|
||||
git_bin: upath.normalizeSafe(path.resolve(binaries_path, "git-bin", "bin", isWin ? "git.exe" : "git")),
|
||||
rclone_bin: upath.normalizeSafe(path.resolve(binaries_path, "rclone-bin", isWin ? "rclone.exe" : "rclone")),
|
||||
java_jre_bin: upath.normalizeSafe(path.resolve(binaries_path, "java_jre_bin", (isMac ? "Contents/Home/bin/java" : (isWin ? "bin/java.exe" : "bin/java")))),
|
||||
}
|
||||
|
||||
export default {
|
||||
runtimeName,
|
||||
db_path,
|
||||
userdata_path,
|
||||
runtime_path,
|
||||
cache_path,
|
||||
packages_path,
|
||||
binaries_path,
|
||||
...binaries,
|
||||
}
|
41
packages/gui/.gitignore
vendored
Normal file
41
packages/gui/.gitignore
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# Secrets
|
||||
/**/**/.env
|
||||
/**/**/origin.server
|
||||
/**/**/server.manifest
|
||||
/**/**/server.registry
|
||||
/**/**/*.secret.*
|
||||
|
||||
/**/**/_shared
|
||||
|
||||
# Trash
|
||||
/**/**/*.log
|
||||
/**/**/dumps.log
|
||||
/**/**/.crash.log
|
||||
/**/**/.tmp
|
||||
/**/**/.cache
|
||||
/**/**/cache
|
||||
/**/**/out
|
||||
/**/**/.out
|
||||
/**/**/dist
|
||||
/**/**/node_modules
|
||||
/**/**/corenode_modules
|
||||
/**/**/.DS_Store
|
||||
/**/**/package-lock.json
|
||||
/**/**/yarn.lock
|
||||
/**/**/.evite
|
||||
/**/**/build
|
||||
/**/**/uploads
|
||||
/**/**/d_data
|
||||
/**/**/*.tar
|
||||
/**/**/*.7z
|
||||
/**/**/*.zip
|
||||
/**/**/*.env
|
||||
|
||||
# Logs
|
||||
/**/**/npm-debug.log*
|
||||
/**/**/yarn-error.log
|
||||
/**/**/dumps.log
|
||||
/**/**/corenode.log
|
||||
|
||||
# Temporal configurations
|
||||
/**/**/.aliaser
|
75
packages/gui/package.json
Normal file
75
packages/gui/package.json
Normal file
@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "@ragestudio/relic-gui",
|
||||
"version": "0.15.0",
|
||||
"description": "RageStudio Relic, yet another package manager.",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "RageStudio",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
||||
"start": "electron-vite preview",
|
||||
"dev": "electron-vite dev",
|
||||
"build": "electron-vite build",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"pack:win": "electron-builder --win --config",
|
||||
"pack:mac": "electron-builder --mac --config",
|
||||
"pack:linux": "electron-builder --linux --config",
|
||||
"build:win": "npm run build && npm run pack:win",
|
||||
"build:mac": "npm run build && npm run pack:mac",
|
||||
"build:linux": "npm run build && npm run pack:linux"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron-toolkit/preload": "^2.0.0",
|
||||
"@electron-toolkit/utils": "^2.0.0",
|
||||
"@getstation/electron-google-oauth2": "^14.0.0",
|
||||
"@imjs/electron-differential-updater": "^5.1.7",
|
||||
"@loadable/component": "^5.16.3",
|
||||
"@ragestudio/hermes": "^0.1.1",
|
||||
"adm-zip": "^0.5.10",
|
||||
"antd": "^5.13.2",
|
||||
"checksum": "^1.0.0",
|
||||
"classnames": "^2.3.2",
|
||||
"electron-differential-updater": "^4.3.2",
|
||||
"electron-is-dev": "^2.0.0",
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "^6.1.1",
|
||||
"googleapis": "^105.0.0",
|
||||
"got": "11.8.3",
|
||||
"human-format": "^1.2.0",
|
||||
"less": "^4.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"merge-stream": "^2.0.0",
|
||||
"node-7z": "^3.0.0",
|
||||
"open": "8.4.2",
|
||||
"progress-stream": "^2.0.0",
|
||||
"protocol-registry": "^1.4.1",
|
||||
"react-icons": "^4.11.0",
|
||||
"react-router-dom": "6.6.2",
|
||||
"react-spinners": "^0.13.8",
|
||||
"react-spring": "^9.7.3",
|
||||
"react-motion": "0.5.2",
|
||||
"request": "^2.88.2",
|
||||
"rimraf": "^5.0.5",
|
||||
"signal-exit": "^4.1.0",
|
||||
"unzipper": "^0.10.14",
|
||||
"upath": "^2.0.1",
|
||||
"uuid": "^9.0.1",
|
||||
"which": "^4.0.0",
|
||||
"winreg": "^1.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/eslint-config": "^1.0.1",
|
||||
"@electron-toolkit/eslint-config-prettier": "^1.0.1",
|
||||
"@vitejs/plugin-react": "^4.0.4",
|
||||
"electron": "^25.6.0",
|
||||
"electron-builder": "^24.6.3",
|
||||
"electron-vite": "^1.0.27",
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"prettier": "^3.0.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"vite": "^4.4.9"
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user