| ; | |
| 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; | |
| } | |
| return found; | |
| } | |
| toJSON(_arg, ctx) { | |
| if (!ctx) | |
| return { source: this.source }; | |
| const { anchors, doc, maxAliasCount } = ctx; | |
| const source = this.resolve(doc, ctx); | |
| if (!source) { | |
| const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; | |
| throw new ReferenceError(msg); | |
| } | |
| let data = anchors.get(source); | |
| if (!data) { | |
| // Resolve anchors for Node.prototype.toJS() | |
| toJS.toJS(source, null, ctx); | |
| data = anchors.get(source); | |
| } | |
| /* 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, source, anchors); | |
| if (data.count * data.aliasCount > maxAliasCount) { | |
| const msg = 'Excessive alias count indicates a resource exhaustion attack'; | |
| throw new ReferenceError(msg); | |
| } | |
| } | |
| return data.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; | |
Xet Storage Details
- Size:
- 3.9 kB
- Xet hash:
- decbafa4b35520a34014a1ca9389ba0e16272017669b0e57afd5b88c1c653d46
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.