@fastly/esi
Version:
ESI implementation for JavaScript, using the modern fetch and streaming APIs.
60 lines (59 loc) • 3 kB
JavaScript
/*
* Copyright Fastly, Inc.
* Licensed under the MIT license. See LICENSE file for details.
*/
// XML Transformation Stream
// Base class for applying transformation to streaming XML.
// As items stream in, we check for completed top level items.
// Whenever one of the items in this gets "completed", it is dispatched. When it is dispatched, it is
// removed from the array.
import { XmlStreamerContext } from "./XmlStreamer.js";
import { XmlElement } from "./XmlModel.js";
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
export default class XmlTransformStream extends TransformStream {
constructor(document, xmlTransformer, ignoreDefaultTags = false) {
const xmlStreamerContext = new XmlStreamerContext(document, { ignoreDefaultTags, beforeProcess: xmlTransformer.xmlStreamerBeforeProcess });
async function dispatchCompleteTopLevelChildren(enqueueFunc) {
while (xmlStreamerContext.children.length > 0) {
const firstChild = xmlStreamerContext.children.shift();
if (firstChild instanceof XmlElement && xmlStreamerContext.openElements.includes(firstChild)) {
// If the item is an open element, we will not dispatch this one yet,
// and break the loop
xmlStreamerContext.children.unshift(firstChild);
break;
}
// Dispatch the item
const transformResult = await xmlTransformer.transformElementNode(firstChild);
const transformResultString = XmlElement.serialize(transformResult);
const chunk = textEncoder.encode(transformResultString);
enqueueFunc(chunk);
}
}
const transformer = {
async transform(chunk, controller) {
// noinspection SuspiciousTypeOfGuard
if (!(chunk instanceof Uint8Array)) {
// Guard anyway in case someone uses this TransformStream with an unexpected stream type
throw new Error('Received non-Uint8Array chunk');
}
let chunkAsString = textDecoder.decode(chunk);
// Whenever a chunk is added, it is added to the currently processing chunk, and an attempt is made to
// parse it.
// Whenever one of the items in this gets "completed", it is dispatched. When it is dispatched, it is
// removed from the array.
xmlStreamerContext.append(chunkAsString);
await dispatchCompleteTopLevelChildren(chunk => {
controller.enqueue(chunk);
});
},
async flush(controller) {
xmlStreamerContext.flush(true);
await dispatchCompleteTopLevelChildren(chunk => {
controller.enqueue(chunk);
});
},
};
super(transformer);
}
}