import { Machine, MachineInit } from "../Independents/Machine.js" import { CHostOwnershipRecord, IHost } from "../Engine/Host.js" import { Datagram, Request, ResponseInit } from "../Independents/Datagram/datagram-types.js" import { HostNameTemplate } from "../Independents/DNS/HostName.js" import { SpawningError } from "../Engine/Gateway.js" import { Either } from "../Independents/util/Either.js"; import { Burpanet } from "../Public/burpanet.js" import { Subscription } from "../Independents/util/Subscription.js" import { DocumentHost } from "./DocumentHost.js"; import { Stream } from "../Independents/util/Stream.js" import { OpenPromise } from "../Independents/util/OpenPromise.js" import { evalESM } from "../Engine/communication/EncodeDecodeMessage.js" if (typeof (globalThis.HTMLElement) == undefined) { globalThis["HTMLElement"] = class HTMLElement { } as any; } export abstract class ElementWithSrc extends HTMLElement implements IHost { src: string = ""; feedbackUrl: string | null = null; async readData() { for await (let d of this.source) { Burpanet.state.log("Updated", `${this.idString()} data='${ElementWithSrc.toString(d)}'`); this.gotData(d); } } gotData(d: any) { } idString() { return `<${this.tagName.toLowerCase()}${this.slot ? ` slot=${this.slot}` : ""}${this.id ? ` id='${this.id}'` : ""}${this.src ? ` src='${this.src}'` : ""}${this._host.aliases2.length > 0 ? ` with hostnames="${this._host.aliases2.join(",")}"` : ""}>`; } _source: Subscription = new Subscription(); feedback: Stream = new Stream(); _host: DocumentHost = new DocumentHost(null, { onRequest: (req: Request): any => { return this.onRequest(req); } }); hostName: OpenPromise = new OpenPromise(); onRequest(request: Request): Promise | ResponseInit { return this.source; } get aliases2() { return [this.id]; } get source(): Subscription { return this._source; } get machine(): Machine { return this._host.machine; } get childHosts(): { [key: string]: CHostOwnershipRecord } { return this.childHosts; } addChild(name: string, secret: string, registed: boolean, gateway: string,): CHostOwnershipRecord { return this.addChild(name, secret, registed, gateway); } allocateChildName(hostTemplate: HostNameTemplate, secret: string): Either { return this.allocateChildName(hostTemplate, secret); } async onReceiveDatagram(datagram: Datagram): Promise { return this._host.onReceiveDatagram(datagram); } async srcFetch(src: string) { //console.warn("about to fetch ", src); let def = this.defaultAccept(); let init = { headers: { ...def && { accept: def } } }; let r = await Burpanet.fetchSubscribed(src, init); //console.log("src fetched", r); for await (let d of r) { if (d.status >= 200 && d.status < 300) { //console.log("setting fetched data in ", this.id, d.body); this.source.setCurrent(d.body); } else { this.source.setCurrent(undefined); } } } attributeChangedCallback(id: string, oldVal: string, newVal: string) { switch (id) { case "host": let p = Burpanet.spawnHost(newVal, this._host.machineInit); p.then((name: string | null) => { if (name) { //console.warn(`Have spawned an ElementWithSource host ${name}`); this.hostName.resolve(name); } else { console.warn(`Failed to spawn ${newVal} spawn returned ${name}`); } }) break; case "src": this.src = newVal; this.srcFetch(newVal); break; case "feedback": this.feedbackUrl = newVal; break; case "mime-type": break; // default: // super.attributeChangedCallback(id, oldVal, newVal); } } defaultAccept() { return "application/json"; } connectedCallback() { for (let att of ["host"]) { let a = this.getAttribute(att); if (a) { this.attributeChangedCallback(att, "", a); } } let data = this.getInlinedData(); // let counter = 0; data.then((value) => { if (value !== undefined) { //console.log(`SETTING INLINED DATA FOR ${this.idString()}`, value); this.source.setCurrent(value); } // setInterval(() => { // counter++; // let value = { counter }; // this.source.setCurrent(value); // }, 1000); }) // if (inline) { // inline.then((module: any) => { // console.log("FOUND DATA", module); // this._content.resolve(module.default); // // this.content.then((data: any) => { // // console.log("FOUND CONTENT", data); // // }); // }); // } else { // //console.log("no inlined data") // } for (let att of ["mime-type", "feedback", "src"]) { let a = this.getAttribute(att); if (a) { this.attributeChangedCallback(att, "", a); } } this.readData(); this.processFeedback(); } async processFeedback() { for await (let f of this.feedback) { console.log("PROCESS FEEDBACK", this.id, f); this.postFeedback(f); } } postFeedback(feedback: any) { let url = this.feedbackUrl ?? this.src; console.warn("POSTING FEEDBACK", url, feedback); let response = Burpanet.fetch(url, { method: "POST", body: feedback }) } async getInlinedData(): Promise { let slot = this.shadowRoot?.firstChild as HTMLSlotElement; let template: HTMLElement | null; if (slot != null) { template = slot.assignedElements()[0] as HTMLElement; } else { template = this.children[0] as HTMLElement | null; } if (template && template instanceof HTMLTemplateElement) { let frag = template.content; if (frag) { let script = frag.firstElementChild as HTMLScriptElement; let text = script.text; // console.log("Got inlined data", text) switch (script.type) { case "module": let module = await evalESM(text); return module.default; case "json": return Promise.resolve(JSON.parse(text)); case "application/javascript": case "": return Promise.resolve(eval(text)); default: console.warn(`Scripts of type = "${script.type}" are not supported.`); return Promise.resolve(eval(text)); } } } return undefined; } static toString(d: any) { if (typeof (d) == "function") { return d.toString(); } return JSON.stringify(d); } }