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";
export { default as streamToString } from "./tostring";
export { TempstreamJSX } from "./jsx";
/* eslint-disable */
declare global {
namespace JSX {
type HtmlAttribute = string | boolean;
type HtmlElement = HTMLTag;
interface IntrinsicElements extends IntrinsicElementMap {}
interface InstrictPropsInterface {
[k: string]: HtmlAttribute | Promise<HtmlAttribute>;
}
type IntrinsicElementMap = {
[K in HtmlElement]: InstrictPropsInterface & {
children?: HtmlElement | HtmlElement[];
};
};
type Element = Readable;
}
}
/* 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 };
async function* templateGen(
strings: TemplateStringsArray,
...params: Templatable[]
) {
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 {
is_piping_substream = false;
is_handling_array = false;
is_finished = false;
has_errors = false;
id = Math.random();
emitter = new EventEmitter();
constructor(public generator: ReturnType<typeof templateGen>) {
super();
}
async _read() {
if (this.is_piping_substream || this.is_handling_array) {
return;
}
try {
const result = await this.generator.next();
const value = result?.value;
if (
typeof value === "object" &&
(value as TemplateGenError)?.ErrorSymbol === ErrorSymbol
) {
this.finishStream("error", (value as TemplateGenError)?.error);
}
await this.handleResult(
result,
(data) => {
this.push(data);
},
() => this.finishStream("success")
);
} catch (e) {
this.finishStream("error", e);
}
}
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
}
async handleSingleResult(subvalue: unknown, push: (data: any) => void) {
if (subvalue instanceof Readable) {
this.is_piping_substream = true;
await pipeInto(subvalue, push)
.then(() => {
this.is_piping_substream = false;
void this._read();
})
.catch((error) => {
this.finishStream("error", error);
});
} else {
push(await stringify(subvalue as Stringifiable));
}
}
async serializePluralResult(value: unknown, push: (data: any) => void) {
value = await value;
if (Array.isArray(value)) {
this.is_handling_array = true;
for (const [index, subvalue] of Object.entries(value)) {
// eslint-disable-next-line no-await-in-loop
await this.serializePluralResult(subvalue, push);
if (parseInt(index) < value.length - 1) push("\n");
}
this.is_handling_array = false;
void this._read();
} else {
await this.handleSingleResult(value, push);
}
}
async handleResult(
result: IteratorResult<unknown>,
push: (data: any) => void,
end: () => void
) {
if (result.done) {
push(null);
end();
}
const value: unknown = result.value;
await this.serializePluralResult(await value, push);
}
push(data: unknown): boolean {
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
Sat, Nov 23, 02:31 (1 d, 7 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
547793
Default Alt Text
index.ts (5 KB)

Event Timeline