create-frootjs 1.0.8 → 1.0.10

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.
@@ -19,7 +19,7 @@ export const streamingTemplate = {
19
19
  start: 'node dist/server.js',
20
20
  },
21
21
  dependencies: {
22
- frootjs: '^1.0.2',
22
+ frootjs: '^1.0.5',
23
23
  },
24
24
  devDependencies: {
25
25
  '@types/node': '^22.10.0',
@@ -29,182 +29,182 @@ export const streamingTemplate = {
29
29
  }, null, 2) + '\n',
30
30
  'tsconfig.json': COMMON_TSCONFIG,
31
31
  '.gitignore': COMMON_GITIGNORE,
32
- 'README.md': `# ${projectName}
33
-
34
- Froot application built with the **Real-Time Streaming & Server-Sent Events (SSE)** pattern.
35
-
36
- ## Features
37
-
38
- - **SSE Telemetry Channel**: Streams real-time metrics (CPU memory, active requests) using \`streamSSE\`
39
- - **AI Token Streamer**: Demonstrates streaming LLM-style markdown tokens chunk-by-chunk using \`streamText\`
40
- - **Live Interactive Dashboard**: Embedded web client displaying real-time graphs and live chat stream
41
-
42
- ## Getting Started
43
-
44
- \`\`\`bash
45
- # 1. Install dependencies
46
- pnpm install
47
-
48
- # 2. Run in development mode
49
- pnpm run dev
50
-
51
- # 3. Open in browser
52
- http://localhost:3000
53
- \`\`\`
32
+ 'README.md': `# ${projectName}
33
+
34
+ Froot application built with the **Real-Time Streaming & Server-Sent Events (SSE)** pattern.
35
+
36
+ ## Features
37
+
38
+ - **SSE Telemetry Channel**: Streams real-time metrics (CPU memory, active requests) using \`streamSSE\`
39
+ - **AI Token Streamer**: Demonstrates streaming LLM-style markdown tokens chunk-by-chunk using \`streamText\`
40
+ - **Live Interactive Dashboard**: Embedded web client displaying real-time graphs and live chat stream
41
+
42
+ ## Getting Started
43
+
44
+ \`\`\`bash
45
+ # 1. Install dependencies
46
+ pnpm install
47
+
48
+ # 2. Run in development mode
49
+ pnpm run dev
50
+
51
+ # 3. Open in browser
52
+ http://localhost:3000
53
+ \`\`\`
54
54
  `,
55
- 'src/server.ts': `import { Froot } from 'frootjs';
56
- import { cors } from 'frootjs/cors';
57
- import { streamSSE, streamText, sleep } from 'frootjs/streaming';
58
-
59
- export const app = new Froot();
60
-
61
- app.use(cors());
62
-
63
- // Embedded Dashboard HTML
64
- app.get('/', (c) => {
65
- return c.html(\`<!DOCTYPE html>
66
- <html lang="en">
67
- <head>
68
- <meta charset="UTF-8">
69
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
70
- <title>Froot Streaming Demo</title>
71
- <style>
72
- * { box-sizing: border-box; margin: 0; padding: 0; }
73
- body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0f172a; color: #f8fafc; padding: 2rem; }
74
- .container { max-width: 800px; margin: 0 auto; display: flex; flex-direction: column; gap: 1.5rem; }
75
- .card { background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 1.5rem; }
76
- h1 { color: #10b981; font-size: 1.75rem; margin-bottom: 0.5rem; }
77
- h2 { font-size: 1.25rem; margin-bottom: 1rem; color: #38bdf8; }
78
- pre { background: #0b0f19; padding: 1rem; border-radius: 6px; overflow-x: auto; font-size: 0.9rem; line-height: 1.4; }
79
- .metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1rem; }
80
- .metric-box { background: #0b0f19; padding: 1rem; border-radius: 6px; text-align: center; }
81
- .metric-value { font-size: 1.5rem; font-weight: bold; color: #10b981; }
82
- .metric-label { font-size: 0.8rem; color: #94a3b8; text-transform: uppercase; margin-top: 0.25rem; }
83
- button { background: #10b981; color: #0f172a; border: none; padding: 0.6rem 1.2rem; border-radius: 6px; font-weight: bold; cursor: pointer; }
84
- button:hover { background: #059669; }
85
- </style>
86
- </head>
87
- <body>
88
- <div class="container">
89
- <div>
90
- <h1>Froot Streaming Engine</h1>
91
- <p style="color: #94a3b8;">High-throughput Server-Sent Events & chunked HTTP streaming</p>
92
- </div>
93
-
94
- <!-- SSE Metrics Card -->
95
- <div class="card">
96
- <h2>Live Telemetry Stream (SSE)</h2>
97
- <div class="metrics">
98
- <div class="metric-box">
99
- <div id="m-memory" class="metric-value">--</div>
100
- <div class="metric-label">Memory (MB)</div>
101
- </div>
102
- <div class="metric-box">
103
- <div id="m-uptime" class="metric-value">--</div>
104
- <div class="metric-label">Uptime (s)</div>
105
- </div>
106
- <div class="metric-box">
107
- <div id="m-count" class="metric-value">0</div>
108
- <div class="metric-label">Events Received</div>
109
- </div>
110
- </div>
111
- <pre id="sse-log">Connecting to /sse/metrics...</pre>
112
- </div>
113
-
114
- <!-- LLM Stream Card -->
115
- <div class="card">
116
- <h2>AI Token Stream Simulation</h2>
117
- <div style="margin-bottom: 1rem;">
118
- <button id="btn-stream">Generate Streaming Text</button>
119
- </div>
120
- <pre id="stream-output">Click button to trigger token-by-token stream...</pre>
121
- </div>
122
- </div>
123
-
124
- <script>
125
- // SSE Stream
126
- let count = 0;
127
- const es = new EventSource('/sse/metrics');
128
- const log = document.getElementById('sse-log');
129
- es.addEventListener('telemetry', (e) => {
130
- const data = JSON.parse(e.data);
131
- count++;
132
- document.getElementById('m-memory').textContent = data.memoryUsageMB;
133
- document.getElementById('m-uptime').textContent = data.uptimeSec;
134
- document.getElementById('m-count').textContent = count;
135
- log.textContent = 'Last event: ' + JSON.stringify(data, null, 2);
136
- });
137
-
138
- // Token Stream
139
- const btn = document.getElementById('btn-stream');
140
- const output = document.getElementById('stream-output');
141
- btn.addEventListener('click', async () => {
142
- btn.disabled = true;
143
- output.textContent = '';
144
- const response = await fetch('/api/chat/stream');
145
- const reader = response.body.getReader();
146
- const decoder = new TextDecoder();
147
- while (true) {
148
- const { value, done } = await reader.read();
149
- if (done) break;
150
- output.textContent += decoder.decode(value);
151
- }
152
- btn.disabled = false;
153
- });
154
- </script>
155
- </body>
156
- </html>\`);
157
- });
158
-
159
- // Real-time SSE Telemetry endpoint
160
- app.get('/sse/metrics', (c) => {
161
- return streamSSE(
162
- c,
163
- async (stream) => {
164
- let eventId = 1;
165
- while (!stream.signal.aborted) {
166
- const mem = process.memoryUsage();
167
- const payload = {
168
- memoryUsageMB: Math.round(mem.rss / 1024 / 1024),
169
- uptimeSec: Math.floor(process.uptime()),
170
- timestamp: new Date().toISOString(),
171
- };
172
-
173
- await stream.writeSSE({
174
- event: 'telemetry',
175
- id: String(eventId++),
176
- data: JSON.stringify(payload),
177
- });
178
-
179
- await sleep(1000, stream.signal);
180
- }
181
- },
182
- { heartbeatMs: 15_000 }
183
- );
184
- });
185
-
186
- // AI Token Streaming Simulation endpoint
187
- app.get('/api/chat/stream', (c) => {
188
- const sampleResponse = [
189
- 'Hello! ', 'I ', 'am ', 'streaming ', 'from ', 'a ', 'Froot ', 'server. ',
190
- 'Each ', 'token ', 'is ', 'dispatched ', 'with ', 'zero-copy ', 'efficiency.\\n\\n',
191
- 'Froot ', 'supports ', 'standards-compliant ', 'WHATWG ', 'ReadableStream ',
192
- 'pipelines ', 'compatible ', 'with ', 'all ', 'modern ', 'edge ', 'runtimes.',
193
- ];
194
-
195
- return streamText(c, async (stream) => {
196
- for (const chunk of sampleResponse) {
197
- if (stream.signal.aborted) break;
198
- await stream.write(chunk);
199
- await sleep(60, stream.signal);
200
- }
201
- });
202
- });
203
-
204
- const PORT = Number(process.env.PORT) || 3000;
205
- app.listen(PORT, () => {
206
- console.log(\`Streaming server running on http://localhost:\${PORT}\`);
207
- });
55
+ 'src/server.ts': `import { Froot } from 'frootjs';
56
+ import { cors } from 'frootjs/cors';
57
+ import { streamSSE, streamText, sleep } from 'frootjs/streaming';
58
+
59
+ export const app = new Froot();
60
+
61
+ app.use(cors());
62
+
63
+ // Embedded Dashboard HTML
64
+ app.get('/', (c) => {
65
+ return c.html(\`<!DOCTYPE html>
66
+ <html lang="en">
67
+ <head>
68
+ <meta charset="UTF-8">
69
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
70
+ <title>Froot Streaming Demo</title>
71
+ <style>
72
+ * { box-sizing: border-box; margin: 0; padding: 0; }
73
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0f172a; color: #f8fafc; padding: 2rem; }
74
+ .container { max-width: 800px; margin: 0 auto; display: flex; flex-direction: column; gap: 1.5rem; }
75
+ .card { background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 1.5rem; }
76
+ h1 { color: #10b981; font-size: 1.75rem; margin-bottom: 0.5rem; }
77
+ h2 { font-size: 1.25rem; margin-bottom: 1rem; color: #38bdf8; }
78
+ pre { background: #0b0f19; padding: 1rem; border-radius: 6px; overflow-x: auto; font-size: 0.9rem; line-height: 1.4; }
79
+ .metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1rem; }
80
+ .metric-box { background: #0b0f19; padding: 1rem; border-radius: 6px; text-align: center; }
81
+ .metric-value { font-size: 1.5rem; font-weight: bold; color: #10b981; }
82
+ .metric-label { font-size: 0.8rem; color: #94a3b8; text-transform: uppercase; margin-top: 0.25rem; }
83
+ button { background: #10b981; color: #0f172a; border: none; padding: 0.6rem 1.2rem; border-radius: 6px; font-weight: bold; cursor: pointer; }
84
+ button:hover { background: #059669; }
85
+ </style>
86
+ </head>
87
+ <body>
88
+ <div class="container">
89
+ <div>
90
+ <h1>Froot Streaming Engine</h1>
91
+ <p style="color: #94a3b8;">High-throughput Server-Sent Events & chunked HTTP streaming</p>
92
+ </div>
93
+
94
+ <!-- SSE Metrics Card -->
95
+ <div class="card">
96
+ <h2>Live Telemetry Stream (SSE)</h2>
97
+ <div class="metrics">
98
+ <div class="metric-box">
99
+ <div id="m-memory" class="metric-value">--</div>
100
+ <div class="metric-label">Memory (MB)</div>
101
+ </div>
102
+ <div class="metric-box">
103
+ <div id="m-uptime" class="metric-value">--</div>
104
+ <div class="metric-label">Uptime (s)</div>
105
+ </div>
106
+ <div class="metric-box">
107
+ <div id="m-count" class="metric-value">0</div>
108
+ <div class="metric-label">Events Received</div>
109
+ </div>
110
+ </div>
111
+ <pre id="sse-log">Connecting to /sse/metrics...</pre>
112
+ </div>
113
+
114
+ <!-- LLM Stream Card -->
115
+ <div class="card">
116
+ <h2>AI Token Stream Simulation</h2>
117
+ <div style="margin-bottom: 1rem;">
118
+ <button id="btn-stream">Generate Streaming Text</button>
119
+ </div>
120
+ <pre id="stream-output">Click button to trigger token-by-token stream...</pre>
121
+ </div>
122
+ </div>
123
+
124
+ <script>
125
+ // SSE Stream
126
+ let count = 0;
127
+ const es = new EventSource('/sse/metrics');
128
+ const log = document.getElementById('sse-log');
129
+ es.addEventListener('telemetry', (e) => {
130
+ const data = JSON.parse(e.data);
131
+ count++;
132
+ document.getElementById('m-memory').textContent = data.memoryUsageMB;
133
+ document.getElementById('m-uptime').textContent = data.uptimeSec;
134
+ document.getElementById('m-count').textContent = count;
135
+ log.textContent = 'Last event: ' + JSON.stringify(data, null, 2);
136
+ });
137
+
138
+ // Token Stream
139
+ const btn = document.getElementById('btn-stream');
140
+ const output = document.getElementById('stream-output');
141
+ btn.addEventListener('click', async () => {
142
+ btn.disabled = true;
143
+ output.textContent = '';
144
+ const response = await fetch('/api/chat/stream');
145
+ const reader = response.body.getReader();
146
+ const decoder = new TextDecoder();
147
+ while (true) {
148
+ const { value, done } = await reader.read();
149
+ if (done) break;
150
+ output.textContent += decoder.decode(value);
151
+ }
152
+ btn.disabled = false;
153
+ });
154
+ </script>
155
+ </body>
156
+ </html>\`);
157
+ });
158
+
159
+ // Real-time SSE Telemetry endpoint
160
+ app.get('/sse/metrics', (c) => {
161
+ return streamSSE(
162
+ c,
163
+ async (stream) => {
164
+ let eventId = 1;
165
+ while (!stream.signal.aborted) {
166
+ const mem = process.memoryUsage();
167
+ const payload = {
168
+ memoryUsageMB: Math.round(mem.rss / 1024 / 1024),
169
+ uptimeSec: Math.floor(process.uptime()),
170
+ timestamp: new Date().toISOString(),
171
+ };
172
+
173
+ await stream.writeSSE({
174
+ event: 'telemetry',
175
+ id: String(eventId++),
176
+ data: JSON.stringify(payload),
177
+ });
178
+
179
+ await sleep(1000, stream.signal);
180
+ }
181
+ },
182
+ { heartbeatMs: 15_000 }
183
+ );
184
+ });
185
+
186
+ // AI Token Streaming Simulation endpoint
187
+ app.get('/api/chat/stream', (c) => {
188
+ const sampleResponse = [
189
+ 'Hello! ', 'I ', 'am ', 'streaming ', 'from ', 'a ', 'Froot ', 'server. ',
190
+ 'Each ', 'token ', 'is ', 'dispatched ', 'with ', 'zero-copy ', 'efficiency.\\n\\n',
191
+ 'Froot ', 'supports ', 'standards-compliant ', 'WHATWG ', 'ReadableStream ',
192
+ 'pipelines ', 'compatible ', 'with ', 'all ', 'modern ', 'edge ', 'runtimes.',
193
+ ];
194
+
195
+ return streamText(c, async (stream) => {
196
+ for (const chunk of sampleResponse) {
197
+ if (stream.signal.aborted) break;
198
+ await stream.write(chunk);
199
+ await sleep(60, stream.signal);
200
+ }
201
+ });
202
+ });
203
+
204
+ const PORT = Number(process.env.PORT) || 3000;
205
+ app.listen(PORT, () => {
206
+ console.log(\`Streaming server running on http://localhost:\${PORT}\`);
207
+ });
208
208
  `,
209
209
  };
210
210
  },