Sindbad~EG File Manager
'use strict';
const Assert = require('@hapi/hoek/assert');
const Any = require('./any');
const Common = require('../common');
const Compile = require('../compile');
const Errors = require('../errors');
const internals = {};
module.exports = Any._extend({
type: 'link',
properties: {
schemaChain: true
},
terms: {
link: { init: null, register: false }
},
args(schema, ref) {
return schema.ref(ref);
},
validate(value, { schema, state, prefs }) {
Assert(schema.$_terms.link, 'Uninitialized link schema');
const linked = internals.generate(schema, value, state, prefs);
const ref = schema.$_terms.link[0].ref;
return linked.$_validate(value, state.nest(linked, `link:${ref.display}:${linked.type}`), prefs);
},
generate(schema, value, state, prefs) {
return internals.generate(schema, value, state, prefs);
},
rules: {
ref: {
method(ref) {
Assert(!this.$_terms.link, 'Cannot reinitialize schema');
ref = Compile.ref(ref);
Assert(ref.type === 'value' || ref.type === 'local', 'Invalid reference type:', ref.type);
Assert(ref.type === 'local' || ref.ancestor === 'root' || ref.ancestor > 0, 'Link cannot reference itself');
const obj = this.clone();
obj.$_terms.link = [{ ref }];
return obj;
}
},
relative: {
method(enabled = true) {
return this.$_setFlag('relative', enabled);
}
}
},
overrides: {
concat(source) {
Assert(this.$_terms.link, 'Uninitialized link schema');
Assert(Common.isSchema(source), 'Invalid schema object');
Assert(source.type !== 'link', 'Cannot merge type link with another link');
const obj = this.clone();
if (!obj.$_terms.whens) {
obj.$_terms.whens = [];
}
obj.$_terms.whens.push({ concat: source });
return obj.$_mutateRebuild();
}
}
});
// Helpers
internals.generate = function (schema, value, state, prefs) {
let linked = state.mainstay.links.get(schema);
if (linked) {
return linked._generate(value, state, prefs).schema;
}
const ref = schema.$_terms.link[0].ref;
const { perspective, path } = internals.perspective(ref, state);
internals.assert(perspective, 'which is outside of schema boundaries', ref, schema, state, prefs);
try {
linked = path.length ? perspective.$_reach(path) : perspective;
}
catch (ignoreErr) {
internals.assert(false, 'to non-existing schema', ref, schema, state, prefs);
}
internals.assert(linked.type !== 'link', 'which is another link', ref, schema, state, prefs);
if (!schema._flags.relative) {
state.mainstay.links.set(schema, linked);
}
return linked._generate(value, state, prefs).schema;
};
internals.perspective = function (ref, state) {
if (ref.type === 'local') {
for (const { schema, key } of state.schemas) { // From parent to root
const id = schema._flags.id || key;
if (id === ref.path[0]) {
return { perspective: schema, path: ref.path.slice(1) };
}
if (schema.$_terms.shared) {
for (const shared of schema.$_terms.shared) {
if (shared._flags.id === ref.path[0]) {
return { perspective: shared, path: ref.path.slice(1) };
}
}
}
}
return { perspective: null, path: null };
}
if (ref.ancestor === 'root') {
return { perspective: state.schemas[state.schemas.length - 1].schema, path: ref.path };
}
return { perspective: state.schemas[ref.ancestor]?.schema, path: ref.path };
};
internals.assert = function (condition, message, ref, schema, state, prefs) {
if (condition) { // Manual check to avoid generating error message on success
return;
}
Assert(false, `"${Errors.label(schema._flags, state, prefs)}" contains link reference "${ref.display}" ${message}`);
};
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists