rip-lang 3.13.83 → 3.13.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/docs/dist/rip.js +152 -132
- package/docs/dist/rip.min.js +140 -161
- package/docs/dist/rip.min.js.br +0 -0
- package/package.json +1 -1
- package/src/browser.js +117 -74
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.
|
|
12
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.85-blue.svg" alt="Version"></a>
|
|
13
13
|
<a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
|
|
14
14
|
<a href="#"><img src="https://img.shields.io/badge/tests-1%2C436%2F1%2C436-brightgreen.svg" alt="Tests"></a>
|
|
15
15
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
|
|
@@ -332,14 +332,14 @@ That's it. All `<script type="text/rip">` tags share scope — `export` makes na
|
|
|
332
332
|
<script type="text/rip" src="components/header.rip"></script>
|
|
333
333
|
<script type="text/rip" src="app.rip"></script>
|
|
334
334
|
|
|
335
|
-
<!--
|
|
336
|
-
<script defer src="/rip/rip.min.js" data-
|
|
335
|
+
<!-- Bundle — fetch all components from a server endpoint -->
|
|
336
|
+
<script defer src="/rip/rip.min.js" data-src="bundle" data-mount="App"></script>
|
|
337
337
|
|
|
338
|
-
<!--
|
|
339
|
-
<script defer src="/rip/rip.min.js" data-
|
|
338
|
+
<!-- Bundle with stash persistence (sessionStorage) -->
|
|
339
|
+
<script defer src="/rip/rip.min.js" data-src="bundle" data-mount="App" data-persist></script>
|
|
340
340
|
|
|
341
|
-
<!--
|
|
342
|
-
<script defer src="/rip/rip.min.js" data-
|
|
341
|
+
<!-- Mix bundles and individual files -->
|
|
342
|
+
<script defer src="/rip/rip.min.js" data-src="/rip/ui bundle header.rip" data-mount="App"></script>
|
|
343
343
|
```
|
|
344
344
|
|
|
345
345
|
Every component has a static `mount(target)` method — `App.mount '#app'` is shorthand for `App.new().mount('#app')`. Target defaults to `'body'`.
|
package/docs/dist/rip.js
CHANGED
|
@@ -3510,6 +3510,13 @@ Expecting ${expected.join(", ")}, got '${this.tokenNames[symbol] || symbol}'`;
|
|
|
3510
3510
|
function isPublicProp(target) {
|
|
3511
3511
|
return Array.isArray(target) && target[0] === "." && target[1] === "this";
|
|
3512
3512
|
}
|
|
3513
|
+
function getMemberType(target) {
|
|
3514
|
+
if (target instanceof String && target.type)
|
|
3515
|
+
return target.type;
|
|
3516
|
+
if (Array.isArray(target) && target[2] instanceof String && target[2].type)
|
|
3517
|
+
return target[2].type;
|
|
3518
|
+
return null;
|
|
3519
|
+
}
|
|
3513
3520
|
function installComponentSupport(CodeGenerator, Lexer2) {
|
|
3514
3521
|
let meta = (node, key) => node instanceof String ? node[key] : undefined;
|
|
3515
3522
|
const origClassify = Lexer2.prototype.classifyKeyword;
|
|
@@ -3918,6 +3925,8 @@ Expecting ${expected.join(", ")}, got '${this.tokenNames[symbol] || symbol}'`;
|
|
|
3918
3925
|
return sexpr.map((item) => this.transformComponentMembers(item));
|
|
3919
3926
|
};
|
|
3920
3927
|
proto.generateComponent = function(head, rest, context, sexpr) {
|
|
3928
|
+
if (this.options.stubComponents)
|
|
3929
|
+
return "class {}";
|
|
3921
3930
|
const [, body] = rest;
|
|
3922
3931
|
this.usesTemplates = true;
|
|
3923
3932
|
this.usesReactivity = true;
|
|
@@ -3956,7 +3965,7 @@ Expecting ${expected.join(", ")}, got '${this.tokenNames[symbol] || symbol}'`;
|
|
|
3956
3965
|
} else if (op === "state") {
|
|
3957
3966
|
const varName = getMemberName(stmt[1]);
|
|
3958
3967
|
if (varName) {
|
|
3959
|
-
stateVars.push({ name: varName, value: stmt[2], isPublic: isPublicProp(stmt[1]) });
|
|
3968
|
+
stateVars.push({ name: varName, value: stmt[2], isPublic: isPublicProp(stmt[1]), type: getMemberType(stmt[1]) });
|
|
3960
3969
|
memberNames.add(varName);
|
|
3961
3970
|
reactiveMembers.add(varName);
|
|
3962
3971
|
}
|
|
@@ -3970,7 +3979,7 @@ Expecting ${expected.join(", ")}, got '${this.tokenNames[symbol] || symbol}'`;
|
|
|
3970
3979
|
} else if (op === "readonly") {
|
|
3971
3980
|
const varName = getMemberName(stmt[1]);
|
|
3972
3981
|
if (varName) {
|
|
3973
|
-
readonlyVars.push({ name: varName, value: stmt[2], isPublic: isPublicProp(stmt[1]) });
|
|
3982
|
+
readonlyVars.push({ name: varName, value: stmt[2], isPublic: isPublicProp(stmt[1]), type: getMemberType(stmt[1]) });
|
|
3974
3983
|
memberNames.add(varName);
|
|
3975
3984
|
}
|
|
3976
3985
|
} else if (op === "=") {
|
|
@@ -5989,37 +5998,29 @@ if (typeof globalThis !== 'undefined') {
|
|
|
5989
5998
|
needsBlank = true;
|
|
5990
5999
|
}
|
|
5991
6000
|
}
|
|
5992
|
-
if (this.
|
|
5993
|
-
if (
|
|
5994
|
-
code += `
|
|
6001
|
+
if (this.usesReactivity && !skip) {
|
|
6002
|
+
if (skipRT) {
|
|
6003
|
+
code += `var { __state, __computed, __effect, __batch, __readonly, __setErrorHandler, __handleError, __catchErrors } = globalThis.__rip;
|
|
5995
6004
|
`;
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
} else {
|
|
5999
|
-
if (this.usesReactivity && !skip) {
|
|
6000
|
-
if (skipRT) {
|
|
6001
|
-
code += `var { __state, __computed, __effect, __batch, __readonly, __setErrorHandler, __handleError, __catchErrors } = globalThis.__rip;
|
|
6005
|
+
} else if (typeof globalThis !== "undefined" && globalThis.__rip) {
|
|
6006
|
+
code += `const { __state, __computed, __effect, __batch, __readonly, __setErrorHandler, __handleError, __catchErrors } = globalThis.__rip;
|
|
6002
6007
|
`;
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
`;
|
|
6006
|
-
} else {
|
|
6007
|
-
code += this.getReactiveRuntime();
|
|
6008
|
-
}
|
|
6009
|
-
needsBlank = true;
|
|
6008
|
+
} else {
|
|
6009
|
+
code += this.getReactiveRuntime();
|
|
6010
6010
|
}
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6011
|
+
needsBlank = true;
|
|
6012
|
+
}
|
|
6013
|
+
if (this.usesTemplates && !skip) {
|
|
6014
|
+
if (skipRT) {
|
|
6015
|
+
code += `var { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __transition, __handleComponentError, __Component } = globalThis.__ripComponent;
|
|
6014
6016
|
`;
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
+
} else if (typeof globalThis !== "undefined" && globalThis.__ripComponent) {
|
|
6018
|
+
code += `const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __transition, __handleComponentError, __Component } = globalThis.__ripComponent;
|
|
6017
6019
|
`;
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
}
|
|
6021
|
-
needsBlank = true;
|
|
6020
|
+
} else {
|
|
6021
|
+
code += this.getComponentRuntime();
|
|
6022
6022
|
}
|
|
6023
|
+
needsBlank = true;
|
|
6023
6024
|
}
|
|
6024
6025
|
if (this.dataSection !== null && this.dataSection !== undefined && !skip) {
|
|
6025
6026
|
code += `var DATA;
|
|
@@ -8701,14 +8702,13 @@ if (typeof globalThis !== 'undefined') {
|
|
|
8701
8702
|
let sourceFile = this.options.filename || "input.rip";
|
|
8702
8703
|
sourceMap = new SourceMapGenerator(file, sourceFile, source);
|
|
8703
8704
|
}
|
|
8704
|
-
let lspMode = this.options.mode === "lsp";
|
|
8705
8705
|
let generator = new CodeGenerator({
|
|
8706
8706
|
dataSection,
|
|
8707
|
-
skipPreamble:
|
|
8707
|
+
skipPreamble: this.options.skipPreamble,
|
|
8708
8708
|
skipRuntimes: this.options.skipRuntimes,
|
|
8709
8709
|
skipExports: this.options.skipExports,
|
|
8710
|
+
stubComponents: this.options.stubComponents,
|
|
8710
8711
|
reactiveVars: this.options.reactiveVars,
|
|
8711
|
-
lspMode,
|
|
8712
8712
|
sourceMap
|
|
8713
8713
|
});
|
|
8714
8714
|
let code = generator.compile(sexpr);
|
|
@@ -8725,11 +8725,6 @@ if (typeof globalThis !== 'undefined') {
|
|
|
8725
8725
|
if (typeTokens) {
|
|
8726
8726
|
dts = emitTypes(typeTokens, sexpr);
|
|
8727
8727
|
}
|
|
8728
|
-
if (lspMode && dts) {
|
|
8729
|
-
code = dts.trimEnd() + `
|
|
8730
|
-
|
|
8731
|
-
` + code;
|
|
8732
|
-
}
|
|
8733
8728
|
return { tokens, sexpr, code, dts, map, reverseMap, data: dataSection, reactiveVars: generator.reactiveVars };
|
|
8734
8729
|
}
|
|
8735
8730
|
compileToJS(source) {
|
|
@@ -8769,33 +8764,9 @@ globalThis.zip ??= (...a) => a[0].map((_, i) => a.map(b => b[i]));
|
|
|
8769
8764
|
function getComponentRuntime() {
|
|
8770
8765
|
return new CodeGenerator({}).getComponentRuntime();
|
|
8771
8766
|
}
|
|
8772
|
-
function getLspRuntimeDeclarations() {
|
|
8773
|
-
return `interface Signal<T> { value: T; read(): T; lock(): Signal<T>; free(): Signal<T>; kill(): T; }
|
|
8774
|
-
interface Computed<T> { readonly value: T; read(): T; lock(): Computed<T>; free(): Computed<T>; kill(): T; }
|
|
8775
|
-
declare function __state<T>(v: T): Signal<T>;
|
|
8776
|
-
declare function __computed<T>(fn: () => T): Computed<T>;
|
|
8777
|
-
declare function __effect(fn: () => void | (() => void)): () => void;
|
|
8778
|
-
declare function __batch<T>(fn: () => T): T;
|
|
8779
|
-
declare function __readonly<T>(v: T): Readonly<{ value: T }>;
|
|
8780
|
-
declare function __setErrorHandler(handler: ((err: any) => void) | null): ((err: any) => void) | null;
|
|
8781
|
-
declare function __handleError(error: any): void;
|
|
8782
|
-
declare function __catchErrors<T extends (...args: any[]) => any>(fn: T): T;
|
|
8783
|
-
declare function __pushComponent(component: any): any;
|
|
8784
|
-
declare function __popComponent(prev: any): void;
|
|
8785
|
-
declare function setContext(key: string, value: any): void;
|
|
8786
|
-
declare function getContext(key: string): any;
|
|
8787
|
-
declare function hasContext(key: string): boolean;
|
|
8788
|
-
declare function __clsx(...args: any[]): string;
|
|
8789
|
-
declare function __lis(arr: number[]): number[];
|
|
8790
|
-
declare function __reconcile(anchor: any, state: any, items: any[], ctx: any, factory: any, keyFn: any, ...outer: any[]): void;
|
|
8791
|
-
declare function __transition(el: any, name: string, dir: string, done?: () => void): void;
|
|
8792
|
-
declare function __handleComponentError(error: any, component: any): void;
|
|
8793
|
-
declare class __Component { constructor(props?: any); mount(target?: any): any; unmount(): void; emit(name: string, detail?: any): void; static mount(target?: any): any; [key: string]: any; }
|
|
8794
|
-
`;
|
|
8795
|
-
}
|
|
8796
8767
|
// src/browser.js
|
|
8797
|
-
var VERSION = "3.13.
|
|
8798
|
-
var BUILD_DATE = "2026-03-03
|
|
8768
|
+
var VERSION = "3.13.84";
|
|
8769
|
+
var BUILD_DATE = "2026-03-04@03:00:25GMT";
|
|
8799
8770
|
if (typeof globalThis !== "undefined") {
|
|
8800
8771
|
if (!globalThis.__rip)
|
|
8801
8772
|
new Function(getReactiveRuntime())();
|
|
@@ -8827,102 +8798,151 @@ declare class __Component { constructor(props?: any); mount(target?: any): any;
|
|
|
8827
8798
|
}
|
|
8828
8799
|
}
|
|
8829
8800
|
if (sources.length > 0) {
|
|
8830
|
-
await Promise.
|
|
8801
|
+
const results = await Promise.allSettled(sources.map(async (s) => {
|
|
8831
8802
|
if (!s.url)
|
|
8832
8803
|
return;
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
return;
|
|
8838
|
-
}
|
|
8804
|
+
const res = await fetch(s.url);
|
|
8805
|
+
if (!res.ok)
|
|
8806
|
+
throw new Error(`${s.url} (${res.status})`);
|
|
8807
|
+
if (s.url.endsWith(".rip")) {
|
|
8839
8808
|
s.code = await res.text();
|
|
8840
|
-
}
|
|
8841
|
-
|
|
8809
|
+
} else {
|
|
8810
|
+
const bundle = await res.json();
|
|
8811
|
+
s.bundle = bundle;
|
|
8842
8812
|
}
|
|
8843
8813
|
}));
|
|
8844
|
-
const
|
|
8845
|
-
|
|
8814
|
+
for (const r of results) {
|
|
8815
|
+
if (r.status === "rejected")
|
|
8816
|
+
console.warn("Rip: fetch failed:", r.reason.message);
|
|
8817
|
+
}
|
|
8818
|
+
const bundles = [];
|
|
8819
|
+
const individual = [];
|
|
8846
8820
|
for (const s of sources) {
|
|
8847
|
-
if (
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
let initial = {};
|
|
8860
|
-
const stateAttr = runtimeTag.getAttribute("data-state");
|
|
8861
|
-
if (stateAttr) {
|
|
8821
|
+
if (s.bundle)
|
|
8822
|
+
bundles.push(s.bundle);
|
|
8823
|
+
else if (s.code)
|
|
8824
|
+
individual.push(s);
|
|
8825
|
+
}
|
|
8826
|
+
const routerAttr = runtimeTag?.getAttribute("data-router");
|
|
8827
|
+
const hasRouter = routerAttr != null;
|
|
8828
|
+
if (hasRouter && bundles.length > 0) {
|
|
8829
|
+
const opts = { skipRuntimes: true, skipExports: true };
|
|
8830
|
+
if (individual.length > 0) {
|
|
8831
|
+
let js = "";
|
|
8832
|
+
for (const s of individual) {
|
|
8862
8833
|
try {
|
|
8863
|
-
|
|
8834
|
+
js += compileToJS(s.code, opts) + `
|
|
8835
|
+
`;
|
|
8836
|
+
} catch (e) {
|
|
8837
|
+
console.error(`Rip compile error in ${s.url || "inline"}:`, e.message);
|
|
8838
|
+
}
|
|
8839
|
+
}
|
|
8840
|
+
if (js) {
|
|
8841
|
+
try {
|
|
8842
|
+
await (0, eval)(`(async()=>{
|
|
8843
|
+
${js}
|
|
8844
|
+
})()`);
|
|
8864
8845
|
} catch (e) {
|
|
8865
|
-
console.error("Rip
|
|
8846
|
+
console.error("Rip runtime error:", e);
|
|
8866
8847
|
}
|
|
8867
8848
|
}
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8849
|
+
}
|
|
8850
|
+
const ui = importRip.modules?.["ui.rip"];
|
|
8851
|
+
if (ui?.launch) {
|
|
8852
|
+
const appBundle = bundles[bundles.length - 1];
|
|
8872
8853
|
const persistAttr = runtimeTag.getAttribute("data-persist");
|
|
8873
|
-
|
|
8874
|
-
|
|
8854
|
+
const launchOpts = { bundle: appBundle, hash: routerAttr === "hash" };
|
|
8855
|
+
if (persistAttr != null)
|
|
8856
|
+
launchOpts.persist = persistAttr === "local" ? "local" : true;
|
|
8857
|
+
await ui.launch("", launchOpts);
|
|
8858
|
+
}
|
|
8859
|
+
} else {
|
|
8860
|
+
const expanded = [];
|
|
8861
|
+
for (const b of bundles) {
|
|
8862
|
+
for (const [name, code] of Object.entries(b.components || {})) {
|
|
8863
|
+
expanded.push({ code, url: name });
|
|
8864
|
+
}
|
|
8865
|
+
if (b.data) {
|
|
8866
|
+
const stateAttr = runtimeTag?.getAttribute("data-state");
|
|
8867
|
+
let initial = {};
|
|
8868
|
+
if (stateAttr) {
|
|
8869
|
+
try {
|
|
8870
|
+
initial = JSON.parse(stateAttr);
|
|
8871
|
+
} catch {}
|
|
8872
|
+
}
|
|
8873
|
+
Object.assign(initial, b.data);
|
|
8874
|
+
runtimeTag?.setAttribute("data-state", JSON.stringify(initial));
|
|
8875
8875
|
}
|
|
8876
8876
|
}
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
8877
|
+
expanded.push(...individual);
|
|
8878
|
+
const opts = { skipRuntimes: true, skipExports: true };
|
|
8879
|
+
const compiled = [];
|
|
8880
|
+
for (const s of expanded) {
|
|
8881
|
+
if (!s.code)
|
|
8882
|
+
continue;
|
|
8883
|
+
try {
|
|
8884
|
+
const js = compileToJS(s.code, opts);
|
|
8885
|
+
compiled.push({ js, url: s.url || "inline" });
|
|
8886
|
+
} catch (e) {
|
|
8887
|
+
console.error(`Rip compile error in ${s.url || "inline"}:`, e.message);
|
|
8888
|
+
}
|
|
8889
|
+
}
|
|
8890
|
+
if (!globalThis.__ripApp && runtimeTag) {
|
|
8891
|
+
const stashFn = globalThis.stash;
|
|
8892
|
+
if (stashFn) {
|
|
8893
|
+
let initial = {};
|
|
8894
|
+
const stateAttr = runtimeTag.getAttribute("data-state");
|
|
8895
|
+
if (stateAttr) {
|
|
8896
|
+
try {
|
|
8897
|
+
initial = JSON.parse(stateAttr);
|
|
8898
|
+
} catch (e) {
|
|
8899
|
+
console.error("Rip: invalid data-state JSON:", e.message);
|
|
8900
|
+
}
|
|
8901
|
+
}
|
|
8902
|
+
const app = stashFn({ data: initial });
|
|
8903
|
+
globalThis.__ripApp = app;
|
|
8904
|
+
if (typeof window !== "undefined")
|
|
8905
|
+
window.app = app;
|
|
8906
|
+
const persistAttr = runtimeTag.getAttribute("data-persist");
|
|
8907
|
+
if (persistAttr != null && globalThis.persistStash) {
|
|
8908
|
+
globalThis.persistStash(app, { local: persistAttr === "local" });
|
|
8909
|
+
}
|
|
8910
|
+
}
|
|
8911
|
+
}
|
|
8912
|
+
if (compiled.length > 0) {
|
|
8913
|
+
let js = compiled.map((c) => c.js).join(`
|
|
8880
8914
|
`);
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8915
|
+
const mount = runtimeTag?.getAttribute("data-mount");
|
|
8916
|
+
if (mount) {
|
|
8917
|
+
const target = runtimeTag.getAttribute("data-target") || "body";
|
|
8918
|
+
js += `
|
|
8885
8919
|
${mount}.mount(${JSON.stringify(target)});`;
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8920
|
+
}
|
|
8921
|
+
try {
|
|
8922
|
+
await (0, eval)(`(async()=>{
|
|
8889
8923
|
${js}
|
|
8890
8924
|
})()`);
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8925
|
+
document.body.classList.add("ready");
|
|
8926
|
+
} catch (e) {
|
|
8927
|
+
if (e instanceof SyntaxError) {
|
|
8928
|
+
console.error(`Rip syntax error in combined output: ${e.message}`);
|
|
8929
|
+
for (const c of compiled) {
|
|
8930
|
+
try {
|
|
8931
|
+
new Function(`(async()=>{
|
|
8898
8932
|
${c.js}
|
|
8899
8933
|
})()`);
|
|
8900
|
-
|
|
8901
|
-
|
|
8934
|
+
} catch (e2) {
|
|
8935
|
+
console.error(` → source: ${c.url}`, e2.message);
|
|
8936
|
+
}
|
|
8902
8937
|
}
|
|
8938
|
+
} else {
|
|
8939
|
+
console.error("Rip runtime error:", e);
|
|
8903
8940
|
}
|
|
8904
|
-
} else {
|
|
8905
|
-
console.error("Rip runtime error:", e);
|
|
8906
8941
|
}
|
|
8907
8942
|
}
|
|
8908
8943
|
}
|
|
8909
8944
|
}
|
|
8910
|
-
|
|
8911
|
-
if (cfg && !globalThis.__ripLaunched) {
|
|
8912
|
-
const ui = importRip.modules?.["ui.rip"];
|
|
8913
|
-
if (ui?.launch) {
|
|
8914
|
-
const url = cfg.getAttribute("data-launch") || "";
|
|
8915
|
-
const hash = cfg.getAttribute("data-hash");
|
|
8916
|
-
const persist = cfg.getAttribute("data-persist");
|
|
8917
|
-
const opts = { hash: hash !== "false" };
|
|
8918
|
-
if (url)
|
|
8919
|
-
opts.bundleUrl = url;
|
|
8920
|
-
if (persist != null)
|
|
8921
|
-
opts.persist = persist === "local" ? "local" : true;
|
|
8922
|
-
await ui.launch("", opts);
|
|
8923
|
-
}
|
|
8924
|
-
}
|
|
8925
|
-
if (runtimeTag?.hasAttribute("data-reload")) {
|
|
8945
|
+
if (runtimeTag?.hasAttribute("data-reload") && !globalThis.__ripLaunched) {
|
|
8926
8946
|
let ready = false;
|
|
8927
8947
|
const es = new EventSource("/watch");
|
|
8928
8948
|
es.addEventListener("connected", () => {
|