godprotocol
Version:
A distributed computing environment
269 lines (209 loc) • 7.72 kB
JavaScript
import fs from "fs";
import { hash } from "../utils/hash";
class Fs {
constructor(oracle) {
this.oracle = oracle;
this.fs = fs;
this.base = __dirname;
}
is_directory=async path=>{
if(!await this.exists(path, {pure:true}))return false;
try {
const stats = this.fs.statSync(`${this.base}/${path}`);
return stats.isDirectory();
} catch (err) {
// console.error(`Error checking path: ${err.message}`);
return false; // Handle error, e.g., path doesn't exist
}
}
exists = async (path, options={}) => {
return this.fs.existsSync(`${this.base}/${path}`);
};
mkdir = async (path) => {
path = `${this.base}/${path}`;
!(await this.exists(path, {pure:true})) && this.fs.mkdirSync(path, { recursive: true });
};
read = async(path)=>{
let content;
try{
content = this.fs.readFileSync(`${this.base}/${path}`, {encoding:'utf-8'})
content = JSON.parse(content)
}catch(e){}
return content;
}
retrieve = async(path, options)=>{
let config, result;
if (options.current){
config = {current: path}
}else{
config = await this.read(`${path}/.config`) || {};
if (!config.current) {
if (options.config){
return
}
}
}
if (options.sibling){
if (config.sibling){
let res = await this.read(`${config.sibling}`)
result = res.content
}
}else{
if (config.current){
let res = await this.read(`${config.current}/.config`)
let value = await this.read(`${config.current}/${res.index-1}`)
if(!value && res.current && !res.current.includes('/')){
value = await this.read(`${config.current}/${res.current}`)
}
if(options.config){
result = typeof value.content === 'string'? JSON.parse(value.content): value.content;
}
}
}
return result
}
write = async(path, content, options={})=>{
let addr;
if (options.config){
await this.mkdir(path);
let conf_path = `${path}/.config`
let hash = await this.oracle.hash(content)
let conf = await this.read(conf_path) || {index: 0}
let obj = {content, sibling: conf.current}
let current = `${path}/${hash}`
conf.current = current;
await this.write(conf_path, JSON.stringify(conf))
await this.mkdir(current)
let current_conff = `${current}/.config`
let conff = await this.read(current_conff) || {index: 0}
await this.write(`${current}/${conff.index}`, JSON.stringify(obj))
conff.index++
await this.write(current_conff, JSON.stringify(conff))
addr = current
}else {
this.fs.writeFileSync(`${this.base}/${path}`, content)
addr = path;
}
return addr;
}
store = async(content, {path, type, hash: hash_, is_instance})=>{
if (!type) type = this.oracle.get_type(content)
let result, hash = hash_;
switch(type){
case 'array':
hash = hash || await this.oracle.hash(content)
let arr_path = `${path}/${hash}`
await this.mkdir(arr_path)
let arr = []
for (let i=0; i< content.length; i++){
let item = content[i]
let item_path = `${arr_path}/${i}`
await this.mkdir(item_path)
item = await this.store(item, {path: item_path})
let conf = await this.read(`${item_path}/.config`)
if(!conf) conf = {index:0}
else conf.index++
await this.write(`${item_path}/${conf.index}`, item)
await this.write(`${item_path}/.config`, JSON.stringify(conf));
arr.push(conf.index)
}
let aconf = await this.read(`${path}/.config`) || {}
let arr_hash = await this.oracle.hash(arr)
let arr_conf = await this.read(`${arr_path}/.config`) || {index: 0}
await this.write(`${arr_path}/${arr_hash}`, JSON.stringify({content:arr, type: is_instance? 'instance': type, previous: arr_conf.current, indice: aconf.indice}))
arr_conf.current = arr_hash
arr_conf.sibling = aconf.current;
await this.write(`${arr_path}/.config`, JSON.stringify(arr_conf));
aconf.current = arr_path;
await this.write(`${path}/.config`, JSON.stringify(aconf))
result = arr_path
break;
case 'twain':
hash = hash || await this.oracle.hash(content)
let twa_path = `${path}/${hash}`
await this.mkdir(twa_path)
let twa = {}
for (let prop in content){
let value = content[prop];
let p_hash = await this.oracle.hash(prop)
let pair_path = `${twa_path}/${p_hash}`
await this.mkdir(pair_path)
let p_addr = await this.store(prop, {path: pair_path})
let val_addr = await this.store(value, {path: pair_path})
let pair = [p_addr, val_addr]
let conf = await this.read(`${pair_path}/.config`)
if (!conf){
conf = {index: 0}
}else if (!Object.keys(conf).includes('index'))conf.index = 0
else {
conf.index++
}
await this.write(`${pair_path}/${conf.index}`, JSON.stringify(pair))
twa[p_hash] = conf.index;
await this.write(`${pair_path}/.config`, JSON.stringify(conf));
}
let pconf = await this.read(`${path}/.config`) || {}
let twa_hash = await this.oracle.hash(twa)
let twa_conf = await this.read(`${twa_path}/.config`) || {index: 0}
await this.write(`${twa_path}/${twa_hash}`, JSON.stringify({content:twa, type: is_instance? 'instance': type, previous: twa_conf.current, indice: pconf.indice}))
twa_conf.current = twa_hash
twa_conf.sibling = pconf.current;
await this.write(`${twa_path}/.config`, JSON.stringify(twa_conf));
pconf.current = twa_path;
await this.write(`${path}/.config`, JSON.stringify(pconf))
result = twa_path
break;
case 'string':
hash = await this.oracle.hash(content)
result = await this.write(`${path}/${hash}`, content)
break
case 'number':
hash = await this.oracle.hash(`0${content}`)
result = await this.write(`${path}/${hash}`, JSON.stringify(content))
break
case 'boolean':
hash = await this.oracle.hash(`0${content}`)
result = await this.write(`${path}/${hash}`, JSON.stringify(content))
break
}
return result;
}
}
class Oracle {
constructor(manager) {
this.manager = manager;
this.fs = new Fs(this);
}
read = async(path, options)=>{
let result = await this.fs.retrieve(path, options)
return result;
}
write = async(path, content, options={})=>{
let type = this.get_type(content), addr;
if(options.config){
return await this.fs.write(path, JSON.stringify(content), {config: true})
}
if (options.hash) path = `${path}/${options.hash}`
addr = await this.fs.store(content, {path, type, has_hash: options.hash, is_instance: options.is_instance})
return addr;
}
hash = async(value, alg)=>{
if (typeof value !== 'string')value = JSON.stringify(value)
return hash(value, alg)
}
sync = async () => {
this.manager.path = `${this.manager.trinity.game}/${this.manager.name}`;
await this.fs.mkdir(this.manager.path)
};
get_type = (content)=>{
let type = typeof content;
if (content == null) return 'void'
if (type === 'object'){
if (Array.isArray(content)) type = 'array'
else if (!content) type = 'void'
else type = 'twain'
}
return type;
}
}
export default Oracle;