theseus/bundled-addons/pithos/vendor/yaml/lib/nodes/Alias.js
Local Dev 4ad95b3c7a Theseus: bundle the Pithos add-on
Ships Pithos (the s3d control panel) as a built-in extension: the dock
menu opens it in a tab served from a loopback port, and "Stop s3d"
shuts the gateway down. Generated by Pithos/scripts/build-theseus-addon.mjs.

yaml is vendored under vendor/yaml/lib rather than node_modules/ or
dist/, because Theseus git-ignores both and a clean-worktree release
build would otherwise ship the add-on without its only dependency.
2026-10-03 19:03:05 +02:00

120 lines
3.9 KiB
JavaScript

'use strict';
var anchors = require('../doc/anchors.js');
var visit = require('../visit.js');
var identity = require('./identity.js');
var Node = require('./Node.js');
var toJS = require('./toJS.js');
class Alias extends Node.NodeBase {
constructor(source) {
super(identity.ALIAS);
this.source = source;
Object.defineProperty(this, 'tag', {
set() {
throw new Error('Alias nodes cannot have tags');
}
});
}
/**
* Resolve the value of this alias within `doc`, finding the last
* instance of the `source` anchor before this node.
*/
resolve(doc, ctx) {
if (ctx?.maxAliasCount === 0)
throw new ReferenceError('Alias resolution is disabled');
let nodes;
if (ctx?.aliasResolveCache) {
nodes = ctx.aliasResolveCache;
}
else {
nodes = [];
visit.visit(doc, {
Node: (_key, node) => {
if (identity.isAlias(node) || identity.hasAnchor(node))
nodes.push(node);
}
});
if (ctx)
ctx.aliasResolveCache = nodes;
}
let found = undefined;
for (const node of nodes) {
if (node === this)
break;
if (node.anchor === this.source)
found = node;
}
if (found && ctx) {
const { anchors, doc, maxAliasCount } = ctx;
let data = anchors.get(found);
if (!data) {
// Resolve anchors for Node.prototype.toJS()
toJS.toJS(found, null, ctx);
data = anchors.get(found);
}
/* istanbul ignore if */
if (data?.res === undefined) {
const msg = 'This should not happen: Alias anchor was not resolved?';
throw new ReferenceError(msg);
}
if (maxAliasCount >= 0) {
data.count += 1;
if (data.aliasCount === 0)
data.aliasCount = getAliasCount(doc, found, anchors);
if (data.count * data.aliasCount > maxAliasCount) {
const msg = 'Excessive alias count indicates a resource exhaustion attack';
throw new ReferenceError(msg);
}
}
}
return found;
}
toJSON(_arg, ctx) {
if (!ctx)
return { source: this.source };
const source = this.resolve(ctx.doc, ctx);
if (!source) {
const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;
throw new ReferenceError(msg);
}
return ctx.anchors.get(source).res;
}
toString(ctx, _onComment, _onChompKeep) {
const src = `*${this.source}`;
if (ctx) {
anchors.anchorIsValid(this.source);
if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) {
const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;
throw new Error(msg);
}
if (ctx.implicitKey)
return `${src} `;
}
return src;
}
}
function getAliasCount(doc, node, anchors) {
if (identity.isAlias(node)) {
const source = node.resolve(doc);
const anchor = anchors && source && anchors.get(source);
return anchor ? anchor.count * anchor.aliasCount : 0;
}
else if (identity.isCollection(node)) {
let count = 0;
for (const item of node.items) {
const c = getAliasCount(doc, item, anchors);
if (c > count)
count = c;
}
return count;
}
else if (identity.isPair(node)) {
const kc = getAliasCount(doc, node.key, anchors);
const vc = getAliasCount(doc, node.value, anchors);
return Math.max(kc, vc);
}
return 1;
}
exports.Alias = Alias;