wonder commited on
Commit
b38cf00
·
1 Parent(s): 0e70a77

add tampermonkey script demo

Browse files
Files changed (1) hide show
  1. sse-demo.js +156 -0
sse-demo.js ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ==UserScript==
2
+ // @name SSE EventSource Fetch
3
+ // @namespace Violentmonkey Scripts
4
+ // @match https://example.org/*
5
+ // @grant GM_xmlhttpRequest
6
+ // @run-at document-start
7
+ // ==/UserScript==
8
+
9
+ let createNanoEvents = () => ({
10
+ emit(event, ...args) {
11
+ for (
12
+ let callbacks = this.events[event] || [],
13
+ i = 0,
14
+ length = callbacks.length;
15
+ i < length;
16
+ i++
17
+ ) {
18
+ callbacks[i](...args)
19
+ }
20
+ },
21
+ events: {},
22
+ on(event, cb) {
23
+ ; (this.events[event] ||= []).push(cb)
24
+ return () => {
25
+ this.events[event] = this.events[event]?.filter(i => cb !== i)
26
+ }
27
+ }
28
+ });
29
+
30
+
31
+ class Streamer {
32
+ constructor() {
33
+ this._buffer = "";
34
+ this.metrics = { sseChunks: 0, sseBytes: 0 };
35
+ this._streamGen = 0;
36
+ this._activeRequest = null; // Store the GM_xmlhttpRequest object
37
+ this._events = createNanoEvents();
38
+ }
39
+
40
+ async startStreaming(streamUrl) {
41
+ const gen = ++this._streamGen;
42
+ this._buffer = "";
43
+ let lastLength = 0;
44
+
45
+ return new Promise((resolve, reject) => {
46
+ this._activeRequest = GM_xmlhttpRequest({
47
+ method: "GET",
48
+ url: streamUrl,
49
+ headers: {
50
+ "Accept": "text/event-stream",
51
+ "Cache-Control": "no-cache"
52
+ },
53
+ // IMPORTANT: We do NOT use responseType: "blob"
54
+ // We keep it as text to read it incrementally
55
+ onreadystatechange: (res) => {
56
+
57
+ if (gen !== this._streamGen) return;
58
+
59
+ // readyState 3 = Partial Data Received
60
+ if (res.readyState >= 3 && res.responseText) {
61
+ const newChunk = res.responseText.slice(lastLength);
62
+ if (newChunk.length > 0) {
63
+ lastLength = res.responseText.length;
64
+ this.metrics.sseChunks++;
65
+ this.metrics.sseBytes += newChunk.length;
66
+ this._parseChunk(newChunk);
67
+ }
68
+ }
69
+ },
70
+ onload: (res) => {
71
+ if (gen === this._streamGen) {
72
+ this._emit('debug', 'Stream complete');
73
+ resolve();
74
+ }
75
+ },
76
+ onerror: (err) => {
77
+ this._emit('error', err);
78
+ reject(err);
79
+ },
80
+ onabort: () => {
81
+ this._emit('debug', 'Stream aborted');
82
+ resolve();
83
+ }
84
+ });
85
+ });
86
+ }
87
+
88
+ abort() {
89
+ if (this._activeRequest) {
90
+ this._activeRequest.abort();
91
+ this._activeRequest = null;
92
+ this._streamGen++;
93
+ }
94
+ }
95
+
96
+ _parseChunk(chunk) {
97
+ this._buffer += chunk;
98
+ const lines = this._buffer.split(/\r?\n/);
99
+ this._buffer = lines.pop();
100
+
101
+ for (const line of lines) {
102
+ const trimmedLine = line.trim();
103
+ if (!trimmedLine) continue;
104
+
105
+ if (trimmedLine.startsWith("data: ")) {
106
+ const data = trimmedLine.slice(6);
107
+ if (data === "[DONE]") return;
108
+ this._emit('data', data);
109
+ } else if (trimmedLine.startsWith("event: ")) {
110
+ const data = trimmedLine.slice(7);
111
+ this._emit('event', data);
112
+ }
113
+ }
114
+ }
115
+
116
+ on(event, cb) { return this._events.on(event, cb); }
117
+ _emit(event, ...args) {
118
+ this._events.emit(event, ...args);
119
+ }
120
+ }
121
+
122
+ (async function () {
123
+
124
+
125
+ let counter = 0
126
+ let events = createNanoEvents();
127
+
128
+ events.on('increase', add => {
129
+ counter += add;
130
+ console.log("counter:", counter);
131
+ });
132
+
133
+ events.emit('increase', 1)
134
+ events.emit("increase", 10);
135
+
136
+ const url = "https://nxdev-org-fastapi-sse.hf.space/stream";
137
+
138
+ let s = new Streamer();
139
+ s.on("data", (data) => {
140
+
141
+ console.log(`[streamer] data:`, data);
142
+ })
143
+ s.on("event", (data) => {
144
+
145
+ console.log(`[streamer] event:`, data);
146
+ });
147
+ s.on("debug", (data) => {
148
+
149
+ console.log(`[streamer] :`, data);
150
+ });
151
+
152
+ s.startStreaming(url);
153
+
154
+ })()
155
+
156
+