Page MenuHomeSealhub

index.ts
No OneTemporary

index.ts

import EventEmitter from "events";
import { Readable } from "stream";
import pipeInto from "./pipe-into";
import { Stringifiable, stringify } from "./stringify";
import { HTMLTag } from "./html";
import type { Argument as ClassNamesArgument } from "classnames";
export { default as streamToString } from "./tostring";
export { TempstreamJSX } from "./jsx";
/* eslint-disable */
declare global {
namespace JSX {
type HtmlAttribute =
| string
| boolean
| ClassNamesArgument
| Array<ClassNamesArgument>;
type HtmlElement = HTMLTag;
interface IntrinsicElements extends IntrinsicElementMap {}
interface InstrictPropsInterface {
[k: string]: HtmlAttribute | Promise<HtmlAttribute>;
}
type IntrinsicElementMap = {
[K in HtmlElement]: InstrictPropsInterface & {
children?: Templatable | Templatable[];
};
};
type Element = Readable;
interface ElementChildrenAttribute {
children: {};
}
}
}
/* eslint-enable */
export type MaybePromise<T> = T | Promise<T>;
export type MaybeArray<T> = T | Array<T>;
export type MaybePromiseMaybeArray<T> =
| T
| T[]
| (T | Promise<T>)[]
| Promise<T>
| Promise<Promise<T> | T[]>
| Promise<T[]>
| Promise<T>[];
export type FlatTemplatable = Stringifiable;
export type Templatable = MaybePromise<
MaybeArray<MaybePromise<FlatTemplatable>>
>;
const ErrorSymbol = Symbol("ErrorSymbol");
let unhandledRejectionHandlerSetup = false;
function setupUnhandledRejection() {
if (unhandledRejectionHandlerSetup) {
return;
}
console.log("Setting up unhandled rejection listener");
process.on("unhandledRejection", (error: Error, promise: unknown) => {
console.error("UNHANDLED REJECTION:");
console.error(error);
console.error("FOR PROMISE:");
console.error(promise);
console.error(`!! You're using tempstream, which encaurages passing promises to template
strings. This is a delicate operation! Make sure you're not doing this:
\`\`\`
const data_promise = fetchData(); // returns a Promise
await sleep(200); // <== the process sleeps, while the above Promise rejects
return tempstream\`some data: \${data_promise.then(data=>data.name)}\` // <== tempstream automatically sets up error handlers, but it's too late!
\`\`\`
The problem with the above example is that the \`await sleep(200)\` allows the
data_promise to fail before tempstream manages to add .catch handlers to
it. Either add the error handlers yourself, or don't do any \`await\`s inbetween
promises runing in the background and returning the tempstream.
`);
process.exit(1);
});
unhandledRejectionHandlerSetup = true;
}
setupUnhandledRejection();
export type TemplateGenError = { ErrorSymbol: symbol; error: Error };
let index = 0;
async function* templateGen(
strings: TemplateStringsArray,
...params: Templatable[]
) {
const n = index++;
for (const string of strings) {
yield string;
const param = params.shift();
try {
yield param;
} catch (error: unknown) {
yield { ErrorSymbol, error };
}
}
yield null;
}
export class TempStream extends Readable {
current_promise: Promise<void> | null = null;
is_finished = false;
has_errors = false;
id = Math.random();
emitter = new EventEmitter();
constructor(public generator: ReturnType<typeof templateGen>) {
super();
}
async _read() {
if (this.current_promise || this.is_finished) {
return;
}
this.current_promise = this.generator.next().then(async (result) => {
if (result.done && !this.is_finished) {
this.push(null);
this.finishStream("success");
return;
}
await this.handleResult(result.value);
this.current_promise = null;
this._read();
});
}
async handleResult(value: unknown) {
if (value === null) {
// do nothing;
} else if (
typeof value === "object" &&
(value as TemplateGenError)?.ErrorSymbol === ErrorSymbol
) {
this.finishStream("error", (value as TemplateGenError)?.error);
} else if (Array.isArray(value)) {
for (const [index, subvalue] of Object.entries(value)) {
// eslint-disable-next-line no-await-in-loop
await this.handleResult(await subvalue);
if (parseInt(index) < value.length - 1) this.push("");
}
} else if (value instanceof Readable) {
await pipeInto(value, (data) => this.push(data)).catch((error) => {
this.finishStream("error", error);
});
} else if (
value !== undefined &&
value !== null &&
typeof value !== "string" &&
value.toString
) {
await this.handleResult(value.toString());
} else {
this.push(value);
}
}
waitUntilFinished() {
return new Promise((resolve) => {
if (this.is_finished) {
resolve(this);
} else {
this.emitter.on("finished", () => resolve(this));
}
});
}
finishStream(status: "error" | "success", error?: unknown) {
if (status === "error") {
this.emit("error", error);
this.emitter.emit("promise-error", error);
this.has_errors = true;
} else {
this.emitter.emit("success");
}
this.emitter.emit("success");
this.emitter.emit("finished");
this.is_finished = true;
this.push(null); // end the stream
}
push(data: unknown): boolean {
if (data instanceof Buffer) {
// console.log("PUSHING", data.toString("utf-8"));
} else {
// console.log("PUSHING", data);
}
super.push(data);
return true;
}
}
export function tempstream(
strings: TemplateStringsArray,
...params: Templatable[]
): Readable {
params.forEach((param) => {
if (param instanceof Promise) {
param.catch((e) => {
console.log("Error caught within tempstream main function");
console.error(e);
});
}
});
return new TempStream(templateGen(strings, ...params));
}

File Metadata

Mime Type
text/x-java
Expires
Fri, Nov 22, 09:39 (1 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
547676
Default Alt Text
index.ts (5 KB)

Event Timeline