api-ape 2.0.0 → 2.2.2

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.
Files changed (89) hide show
  1. package/README.md +203 -124
  2. package/client/README.md +37 -30
  3. package/client/browser.js +10 -8
  4. package/client/connectSocket.js +662 -381
  5. package/client/index.js +171 -0
  6. package/client/transports/streaming.js +240 -0
  7. package/dist/ape.js +2 -699
  8. package/dist/ape.js.map +7 -0
  9. package/dist/api-ape.min.js +2 -0
  10. package/dist/api-ape.min.js.map +7 -0
  11. package/index.d.ts +71 -18
  12. package/package.json +50 -15
  13. package/server/README.md +99 -13
  14. package/server/lib/broadcast.js +25 -8
  15. package/server/lib/bun.js +122 -0
  16. package/server/lib/longPolling.js +226 -0
  17. package/server/lib/main.js +381 -38
  18. package/server/lib/wiring.js +19 -12
  19. package/server/lib/ws/adapters/bun.js +225 -0
  20. package/server/lib/ws/adapters/deno.js +186 -0
  21. package/server/lib/ws/frames.js +217 -0
  22. package/server/lib/ws/index.js +15 -0
  23. package/server/lib/ws/server.js +109 -0
  24. package/server/lib/ws/socket.js +222 -0
  25. package/server/lib/wsProvider.js +135 -0
  26. package/server/security/origin.js +16 -4
  27. package/server/socket/receive.js +14 -1
  28. package/server/socket/send.js +6 -6
  29. package/server/utils/deepRequire.js +25 -10
  30. package/server/utils/parseUserAgent.js +286 -0
  31. package/example/Bun/README.md +0 -74
  32. package/example/Bun/api/message.ts +0 -11
  33. package/example/Bun/index.html +0 -76
  34. package/example/Bun/package.json +0 -9
  35. package/example/Bun/server.ts +0 -59
  36. package/example/Bun/styles.css +0 -128
  37. package/example/ExpressJs/README.md +0 -95
  38. package/example/ExpressJs/api/message.js +0 -11
  39. package/example/ExpressJs/backend.js +0 -39
  40. package/example/ExpressJs/index.html +0 -88
  41. package/example/ExpressJs/package-lock.json +0 -834
  42. package/example/ExpressJs/package.json +0 -10
  43. package/example/ExpressJs/styles.css +0 -128
  44. package/example/NextJs/.dockerignore +0 -29
  45. package/example/NextJs/Dockerfile +0 -52
  46. package/example/NextJs/Dockerfile.dev +0 -27
  47. package/example/NextJs/README.md +0 -113
  48. package/example/NextJs/ape/client.js +0 -66
  49. package/example/NextJs/ape/embed.js +0 -12
  50. package/example/NextJs/ape/index.js +0 -23
  51. package/example/NextJs/ape/logic/chat.js +0 -62
  52. package/example/NextJs/ape/onConnect.js +0 -69
  53. package/example/NextJs/ape/onDisconnect.js +0 -13
  54. package/example/NextJs/ape/onError.js +0 -9
  55. package/example/NextJs/ape/onReceive.js +0 -15
  56. package/example/NextJs/ape/onSend.js +0 -15
  57. package/example/NextJs/api/message.js +0 -44
  58. package/example/NextJs/docker-compose.yml +0 -22
  59. package/example/NextJs/next-env.d.ts +0 -5
  60. package/example/NextJs/next.config.js +0 -8
  61. package/example/NextJs/package-lock.json +0 -6400
  62. package/example/NextJs/package.json +0 -24
  63. package/example/NextJs/pages/Info.tsx +0 -153
  64. package/example/NextJs/pages/_app.tsx +0 -6
  65. package/example/NextJs/pages/index.tsx +0 -275
  66. package/example/NextJs/public/favicon.ico +0 -0
  67. package/example/NextJs/public/vercel.svg +0 -4
  68. package/example/NextJs/server.js +0 -36
  69. package/example/NextJs/styles/Chat.module.css +0 -448
  70. package/example/NextJs/styles/Home.module.css +0 -129
  71. package/example/NextJs/styles/globals.css +0 -26
  72. package/example/NextJs/tsconfig.json +0 -20
  73. package/example/README.md +0 -117
  74. package/example/Vite/README.md +0 -68
  75. package/example/Vite/ape/client.ts +0 -66
  76. package/example/Vite/ape/onConnect.ts +0 -52
  77. package/example/Vite/api/message.ts +0 -57
  78. package/example/Vite/index.html +0 -16
  79. package/example/Vite/package.json +0 -19
  80. package/example/Vite/server.ts +0 -62
  81. package/example/Vite/src/App.vue +0 -170
  82. package/example/Vite/src/components/Info.vue +0 -352
  83. package/example/Vite/src/main.ts +0 -5
  84. package/example/Vite/src/style.css +0 -200
  85. package/example/Vite/src/vite-env.d.ts +0 -7
  86. package/example/Vite/vite.config.ts +0 -20
  87. package/todo.md +0 -85
  88. package/utils/jss.test.js +0 -261
  89. package/utils/messageHash.test.js +0 -56
@@ -1,448 +0,0 @@
1
- .container {
2
- min-height: 100vh;
3
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
4
- color: #fff;
5
- font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif;
6
- }
7
-
8
- .main {
9
- max-width: 1400px;
10
- margin: 0 auto;
11
- padding: 2rem;
12
- width: 100%;
13
- }
14
-
15
- .title {
16
- font-size: 2.5rem;
17
- text-align: center;
18
- margin-bottom: 0.5rem;
19
- }
20
-
21
- .gradient {
22
- background: linear-gradient(90deg, #00d2ff, #3a7bd5);
23
- -webkit-background-clip: text;
24
- -webkit-text-fill-color: transparent;
25
- background-clip: text;
26
- }
27
-
28
- .subtitle {
29
- text-align: center;
30
- color: #0f0;
31
- margin-bottom: 2rem;
32
- font-weight: bold;
33
- }
34
-
35
- .joinForm {
36
- display: flex;
37
- gap: 1rem;
38
- justify-content: center;
39
- }
40
-
41
- .input {
42
- padding: 1rem 1.5rem;
43
- font-size: 1rem;
44
- border: none;
45
- border-radius: 50px;
46
- background: rgba(255, 255, 255, 0.1);
47
- color: #fff;
48
- outline: none;
49
- width: 250px;
50
- }
51
-
52
- .input::placeholder {
53
- color: rgba(255, 255, 255, 0.5);
54
- }
55
-
56
- .button {
57
- padding: 1rem 2rem;
58
- font-size: 1rem;
59
- border: none;
60
- border-radius: 50px;
61
- background: linear-gradient(90deg, #00d2ff, #3a7bd5);
62
- color: #fff;
63
- cursor: pointer;
64
- font-weight: bold;
65
- }
66
-
67
- .button:hover {
68
- opacity: 0.9;
69
- }
70
-
71
- .chatContainer {
72
- background: rgba(255, 255, 255, 0.05);
73
- border-radius: 20px;
74
- overflow: hidden;
75
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
76
- }
77
-
78
- .header {
79
- display: flex;
80
- justify-content: space-between;
81
- padding: 1rem 1.5rem;
82
- background: rgba(255, 255, 255, 0.1);
83
- font-weight: bold;
84
- }
85
-
86
- .userCount {
87
- font-size: 0.85rem;
88
- color: #0f0;
89
- }
90
-
91
- .messages {
92
- height: 300px;
93
- overflow-y: auto;
94
- padding: 1rem;
95
- }
96
-
97
- .emptyState {
98
- text-align: center;
99
- color: #666;
100
- margin-top: 120px;
101
- }
102
-
103
- .message {
104
- display: flex;
105
- flex-direction: column;
106
- gap: 0.25rem;
107
- padding: 0.75rem 1rem;
108
- margin-bottom: 0.5rem;
109
- background: rgba(255, 255, 255, 0.05);
110
- border-radius: 12px;
111
- border-left: 3px solid #3a7bd5;
112
- }
113
-
114
- .myMessage {
115
- background: rgba(0, 210, 255, 0.15);
116
- border-left: 3px solid #00d2ff;
117
- }
118
-
119
- .username {
120
- color: #00d2ff;
121
- font-size: 0.85rem;
122
- }
123
-
124
- .time {
125
- color: #666;
126
- font-size: 0.7rem;
127
- }
128
-
129
- .inputForm {
130
- display: flex;
131
- gap: 0.5rem;
132
- padding: 1rem;
133
- border-top: 1px solid rgba(255, 255, 255, 0.1);
134
- }
135
-
136
- .messageInput {
137
- flex: 1;
138
- padding: 0.75rem 1rem;
139
- font-size: 1rem;
140
- border: none;
141
- border-radius: 50px;
142
- background: rgba(255, 255, 255, 0.1);
143
- color: #fff;
144
- outline: none;
145
- }
146
-
147
- .messageInput::placeholder {
148
- color: rgba(255, 255, 255, 0.5);
149
- }
150
-
151
- .sendButton {
152
- padding: 0.75rem 1.5rem;
153
- font-size: 1rem;
154
- border: none;
155
- border-radius: 50px;
156
- background: #3a7bd5;
157
- color: #fff;
158
- cursor: pointer;
159
- font-weight: bold;
160
- }
161
-
162
- .sendButton:hover {
163
- opacity: 0.9;
164
- }
165
-
166
- .codeSection {
167
- margin-top: 2rem;
168
- }
169
-
170
- .codeTitle {
171
- margin-bottom: 0.5rem;
172
- color: #0f0;
173
- }
174
-
175
- .code {
176
- background: rgba(0, 0, 0, 0.4);
177
- padding: 1.5rem;
178
- border-radius: 12px;
179
- font-size: 0.75rem;
180
- color: #0f0;
181
- overflow: auto;
182
- }
183
-
184
- .clientInfo {
185
- text-align: center;
186
- margin-top: 1rem;
187
- font-size: 0.85rem;
188
- color: #888;
189
- }
190
-
191
- .clientInfo code {
192
- background: rgba(0, 0, 0, 0.3);
193
- padding: 0.2rem 0.5rem;
194
- border-radius: 4px;
195
- }
196
-
197
- .gridContainer {
198
- max-width: 1200px;
199
- margin: 1.5rem auto 0;
200
- padding: 0 1rem;
201
- width: 100%;
202
- box-sizing: border-box;
203
- }
204
-
205
- .gridLayout {
206
- display: grid;
207
- grid-template-columns: 1fr;
208
- gap: 2rem;
209
- width: 100%;
210
- }
211
-
212
- @media (min-width: 768px) {
213
- .gridLayout {
214
- grid-template-columns: 1fr 1fr;
215
- }
216
- }
217
-
218
- @media (max-width: 767px) {
219
- .gridContainer {
220
- padding: 0 0.5rem;
221
- }
222
-
223
- .main {
224
- padding: 1rem;
225
- }
226
- }
227
-
228
- /* Info component styles */
229
- .sectionHeading {
230
- margin-bottom: 0.5rem;
231
- font-size: 0.9rem;
232
- font-weight: bold;
233
- }
234
-
235
- .sectionHeadingLarge {
236
- margin-bottom: 1rem;
237
- font-size: 0.9rem;
238
- font-weight: bold;
239
- }
240
-
241
- .dataFlowGrid {
242
- display: grid;
243
- grid-template-columns: 200px 1fr 200px;
244
- grid-template-rows: auto auto auto auto auto;
245
- gap: 1rem;
246
- align-items: stretch;
247
- }
248
-
249
- .columnHeaderClient {
250
- font-size: 0.8rem;
251
- font-weight: bold;
252
- text-align: center;
253
- grid-row: 1;
254
- grid-column: 1;
255
- color: #00d2ff;
256
- }
257
-
258
- .columnHeaderServer {
259
- font-size: 0.8rem;
260
- font-weight: bold;
261
- text-align: center;
262
- grid-row: 1;
263
- grid-column: 3;
264
- color: #00e676;
265
- }
266
-
267
- .gridCell {
268
- grid-row: 1;
269
- grid-column: 2;
270
- }
271
-
272
- .clientBoxSpan3 {
273
- background: linear-gradient(135deg, #3a7bd5, #00d2ff);
274
- padding: 0.75rem 1rem;
275
- border-radius: 8px;
276
- color: #fff;
277
- font-size: 0.75rem;
278
- font-weight: bold;
279
- box-shadow: 0 4px 12px rgba(58, 123, 213, 0.4);
280
- display: flex;
281
- align-items: center;
282
- justify-content: center;
283
- grid-column: 1;
284
- grid-row: 2 / 5;
285
- }
286
-
287
- .clientBoxSingle {
288
- background: linear-gradient(135deg, #3a7bd5, #00d2ff);
289
- padding: 0.75rem 1rem;
290
- border-radius: 8px;
291
- color: #fff;
292
- font-size: 0.75rem;
293
- font-weight: bold;
294
- box-shadow: 0 4px 12px rgba(58, 123, 213, 0.4);
295
- display: flex;
296
- align-items: center;
297
- justify-content: center;
298
- grid-column: 1;
299
- grid-row: 5;
300
- }
301
-
302
- .serverBoxSpan2 {
303
- background: linear-gradient(135deg, #00c851, #00e676);
304
- padding: 0.75rem 1rem;
305
- border-radius: 8px;
306
- color: #fff;
307
- font-size: 0.75rem;
308
- font-weight: bold;
309
- box-shadow: 0 4px 12px rgba(0, 200, 81, 0.4);
310
- display: flex;
311
- align-items: center;
312
- justify-content: center;
313
- grid-column: 3;
314
- grid-row: 2 / 4;
315
- }
316
-
317
- .serverBoxSpan3 {
318
- background: linear-gradient(135deg, #00c851, #00e676);
319
- padding: 0.75rem 1rem;
320
- border-radius: 8px;
321
- color: #fff;
322
- font-size: 0.75rem;
323
- font-weight: bold;
324
- box-shadow: 0 4px 12px rgba(0, 200, 81, 0.4);
325
- display: flex;
326
- align-items: center;
327
- justify-content: center;
328
- grid-column: 3;
329
- grid-row: 4 / 6;
330
- }
331
-
332
- .arrowContainerRow2 {
333
- display: flex;
334
- align-items: center;
335
- justify-content: center;
336
- gap: 0.5rem;
337
- grid-column: 2;
338
- grid-row: 2;
339
- }
340
-
341
- .arrowContainerRow3 {
342
- display: flex;
343
- align-items: center;
344
- justify-content: center;
345
- gap: 0.5rem;
346
- grid-column: 2;
347
- grid-row: 3;
348
- }
349
-
350
- .arrowContainerRow4 {
351
- display: flex;
352
- align-items: center;
353
- justify-content: center;
354
- gap: 0.5rem;
355
- grid-column: 2;
356
- grid-row: 4;
357
- }
358
-
359
- .arrowContainerRow5 {
360
- display: flex;
361
- align-items: center;
362
- justify-content: center;
363
- gap: 0.5rem;
364
- grid-column: 2;
365
- grid-row: 5;
366
- }
367
-
368
- .arrowLineSend {
369
- flex: 1;
370
- height: 2px;
371
- background: linear-gradient(90deg, #00d2ff, #00e676);
372
- }
373
-
374
- .arrowLineReturn {
375
- flex: 1;
376
- height: 2px;
377
- background: linear-gradient(90deg, #00e676, transparent);
378
- }
379
-
380
- .arrowLineBroadcast {
381
- flex: 1;
382
- height: 2px;
383
- background: linear-gradient(90deg, transparent, #00e676);
384
- }
385
-
386
- .arrowLineBroadcastReturn {
387
- flex: 1;
388
- height: 2px;
389
- background: linear-gradient(90deg, #00d2ff, transparent);
390
- }
391
-
392
- .arrowLabelBlue {
393
- font-size: 0.7rem;
394
- white-space: nowrap;
395
- padding: 0 0.5rem;
396
- color: #00d2ff;
397
- }
398
-
399
- .arrowLabelGreen {
400
- font-size: 0.7rem;
401
- white-space: nowrap;
402
- padding: 0 0.5rem;
403
- color: #00e676;
404
- }
405
-
406
- .arrowHeadRight {
407
- width: 0;
408
- height: 0;
409
- border-top: 4px solid transparent;
410
- border-bottom: 4px solid transparent;
411
- border-left: 8px solid #00e676;
412
- }
413
-
414
- .arrowHeadLeft {
415
- width: 0;
416
- height: 0;
417
- border-top: 4px solid transparent;
418
- border-bottom: 4px solid transparent;
419
- border-right: 8px solid #00e676;
420
- }
421
-
422
- .arrowHeadLeftBlue {
423
- width: 0;
424
- height: 0;
425
- border-top: 4px solid transparent;
426
- border-bottom: 4px solid transparent;
427
- border-right: 8px solid #00d2ff;
428
- }
429
-
430
- .emptyGridCell {
431
- grid-row: 2;
432
- grid-column: 3;
433
- }
434
-
435
- .emptyGridCellRow3 {
436
- grid-row: 3;
437
- grid-column: 1;
438
- }
439
-
440
- .emptyGridCellRow4 {
441
- grid-row: 4;
442
- grid-column: 1;
443
- }
444
-
445
- .emptyGridCellRow5 {
446
- grid-row: 5;
447
- grid-column: 3;
448
- }
@@ -1,129 +0,0 @@
1
- .container {
2
- padding: 0 2rem;
3
- }
4
-
5
- .main {
6
- min-height: 100vh;
7
- padding: 4rem 0;
8
- flex: 1;
9
- display: flex;
10
- flex-direction: column;
11
- justify-content: center;
12
- align-items: center;
13
- }
14
-
15
- .footer {
16
- display: flex;
17
- flex: 1;
18
- padding: 2rem 0;
19
- border-top: 1px solid #eaeaea;
20
- justify-content: center;
21
- align-items: center;
22
- }
23
-
24
- .footer a {
25
- display: flex;
26
- justify-content: center;
27
- align-items: center;
28
- flex-grow: 1;
29
- }
30
-
31
- .title a {
32
- color: #0070f3;
33
- text-decoration: none;
34
- }
35
-
36
- .title a:hover,
37
- .title a:focus,
38
- .title a:active {
39
- text-decoration: underline;
40
- }
41
-
42
- .title {
43
- margin: 0;
44
- line-height: 1.15;
45
- font-size: 4rem;
46
- }
47
-
48
- .title,
49
- .description {
50
- text-align: center;
51
- }
52
-
53
- .description {
54
- margin: 4rem 0;
55
- line-height: 1.5;
56
- font-size: 1.5rem;
57
- }
58
-
59
- .code {
60
- background: #fafafa;
61
- border-radius: 5px;
62
- padding: 0.75rem;
63
- font-size: 1.1rem;
64
- font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
65
- Bitstream Vera Sans Mono, Courier New, monospace;
66
- }
67
-
68
- .grid {
69
- display: flex;
70
- align-items: center;
71
- justify-content: center;
72
- flex-wrap: wrap;
73
- max-width: 800px;
74
- }
75
-
76
- .card {
77
- margin: 1rem;
78
- padding: 1.5rem;
79
- text-align: left;
80
- color: inherit;
81
- text-decoration: none;
82
- border: 1px solid #eaeaea;
83
- border-radius: 10px;
84
- transition: color 0.15s ease, border-color 0.15s ease;
85
- max-width: 300px;
86
- }
87
-
88
- .card:hover,
89
- .card:focus,
90
- .card:active {
91
- color: #0070f3;
92
- border-color: #0070f3;
93
- }
94
-
95
- .card h2 {
96
- margin: 0 0 1rem 0;
97
- font-size: 1.5rem;
98
- }
99
-
100
- .card p {
101
- margin: 0;
102
- font-size: 1.25rem;
103
- line-height: 1.5;
104
- }
105
-
106
- .logo {
107
- height: 1em;
108
- margin-left: 0.5rem;
109
- }
110
-
111
- @media (max-width: 600px) {
112
- .grid {
113
- width: 100%;
114
- flex-direction: column;
115
- }
116
- }
117
-
118
- @media (prefers-color-scheme: dark) {
119
- .card,
120
- .footer {
121
- border-color: #222;
122
- }
123
- .code {
124
- background: #111;
125
- }
126
- .logo img {
127
- filter: invert(1);
128
- }
129
- }
@@ -1,26 +0,0 @@
1
- html,
2
- body {
3
- padding: 0;
4
- margin: 0;
5
- font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
6
- Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
7
- }
8
-
9
- a {
10
- color: inherit;
11
- text-decoration: none;
12
- }
13
-
14
- * {
15
- box-sizing: border-box;
16
- }
17
-
18
- @media (prefers-color-scheme: dark) {
19
- html {
20
- color-scheme: dark;
21
- }
22
- body {
23
- color: white;
24
- background: black;
25
- }
26
- }
@@ -1,20 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es5",
4
- "lib": ["dom", "dom.iterable", "esnext"],
5
- "allowJs": true,
6
- "skipLibCheck": true,
7
- "strict": true,
8
- "forceConsistentCasingInFileNames": true,
9
- "noEmit": true,
10
- "esModuleInterop": true,
11
- "module": "esnext",
12
- "moduleResolution": "node",
13
- "resolveJsonModule": true,
14
- "isolatedModules": true,
15
- "jsx": "preserve",
16
- "incremental": true
17
- },
18
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19
- "exclude": ["node_modules"]
20
- }
package/example/README.md DELETED
@@ -1,117 +0,0 @@
1
- # 🦍 api-ape Examples
2
-
3
- Complete working examples demonstrating api-ape usage.
4
-
5
- ## Examples
6
-
7
- | Example | Description | Complexity |
8
- |---------|-------------|------------|
9
- | [ExpressJs/](./ExpressJs/) | Basic real-time chat | Minimal setup |
10
- | [NextJs/](./NextJs/) | Full-featured chat app | Production-ready |
11
- | [Bun/](./Bun/) | Bun + Vue (CDN) | Minimal setup |
12
- | [Vite/](./Vite/) | Vite + Vue + TypeScript | Modern tooling |
13
-
14
- ---
15
-
16
- ## ExpressJs — Basic Example
17
-
18
- A minimal real-time chat demonstrating core api-ape concepts.
19
-
20
- **Features:**
21
- - Simple Express.js server with api-ape
22
- - Broadcast messages to other clients
23
- - Message history
24
-
25
- **Quick Start:**
26
- ```bash
27
- cd ExpressJs
28
- npm install
29
- npm start
30
- ```
31
-
32
- **Key Files:**
33
- - `backend.js` — Server setup (22 lines)
34
- - `api/message.js` — Message handler with `this.broadcastOthers()`
35
- - `index.html` — Browser client using `window.ape`
36
-
37
- ---
38
-
39
- ## NextJs — Complete Example
40
-
41
- A production-ready chat application with Next.js integration.
42
-
43
- **Features:**
44
- - Custom Next.js server with api-ape
45
- - React hooks integration
46
- - User presence tracking
47
- - Docker support
48
- - Connection lifecycle hooks
49
-
50
- **Quick Start:**
51
- ```bash
52
- cd NextJs
53
- npm install
54
- npm run dev
55
- ```
56
-
57
- **Or with Docker:**
58
- ```bash
59
- cd NextJs
60
- docker-compose up --build
61
- ```
62
-
63
- **Key Files:**
64
- - `server.js` — Custom Next.js server with api-ape
65
- - `api/message.js` — Message controller with validation
66
- - `ape/client.js` — React client wrapper
67
- - `ape/onConnect.js` — Connection lifecycle hooks
68
- - `pages/index.tsx` — Chat UI with React hooks
69
-
70
- ---
71
-
72
- ## Bun — Vue CDN Example
73
-
74
- A lightweight Bun server with Vue 3 via CDN — no build step required.
75
-
76
- **Features:**
77
- - Bun runtime (TypeScript native)
78
- - Vue 3 Composition API
79
- - Single HTML file frontend
80
-
81
- **Quick Start:**
82
- ```bash
83
- cd Bun
84
- bun install
85
- bun run server.ts
86
- ```
87
-
88
- **Key Files:**
89
- - `server.ts` — Bun HTTP server with api-ape
90
- - `index.html` — Vue 3 app via CDN
91
- - `api/message.js` — Message handler
92
-
93
- ---
94
-
95
- ## Vite — Vue + TypeScript Example
96
-
97
- A modern Vite + Vue 3 frontend with Bun backend.
98
-
99
- **Features:**
100
- - Vue 3 with TypeScript
101
- - Vite dev server with HMR
102
- - Component-based architecture
103
- - Production build support
104
-
105
- **Quick Start:**
106
- ```bash
107
- cd Vite
108
- npm install
109
- npm run dev # Backend on :3000
110
- npm run dev:vue # Frontend on :5173
111
- ```
112
-
113
- **Key Files:**
114
- - `server.ts` — Bun backend server
115
- - `src/App.vue` — Main Vue component
116
- - `vite.config.ts` — Vite config with API proxy
117
-