api-ape 0.0.0 → 1.0.1

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 (63) hide show
  1. package/README.md +261 -0
  2. package/client/README.md +69 -0
  3. package/client/browser.js +17 -0
  4. package/client/connectSocket.js +260 -0
  5. package/dist/ape.js +454 -0
  6. package/example/ExpressJs/README.md +97 -0
  7. package/example/ExpressJs/api/message.js +11 -0
  8. package/example/ExpressJs/backend.js +37 -0
  9. package/example/ExpressJs/index.html +88 -0
  10. package/example/ExpressJs/package-lock.json +834 -0
  11. package/example/ExpressJs/package.json +10 -0
  12. package/example/ExpressJs/styles.css +128 -0
  13. package/example/NextJs/.dockerignore +29 -0
  14. package/example/NextJs/Dockerfile +52 -0
  15. package/example/NextJs/Dockerfile.dev +27 -0
  16. package/example/NextJs/README.md +113 -0
  17. package/example/NextJs/ape/client.js +66 -0
  18. package/example/NextJs/ape/embed.js +12 -0
  19. package/example/NextJs/ape/index.js +23 -0
  20. package/example/NextJs/ape/logic/chat.js +62 -0
  21. package/example/NextJs/ape/onConnect.js +69 -0
  22. package/example/NextJs/ape/onDisconnect.js +13 -0
  23. package/example/NextJs/ape/onError.js +9 -0
  24. package/example/NextJs/ape/onReceive.js +15 -0
  25. package/example/NextJs/ape/onSend.js +15 -0
  26. package/example/NextJs/api/message.js +44 -0
  27. package/example/NextJs/docker-compose.yml +22 -0
  28. package/example/NextJs/next-env.d.ts +5 -0
  29. package/example/NextJs/next.config.js +8 -0
  30. package/example/NextJs/package-lock.json +5107 -0
  31. package/example/NextJs/package.json +25 -0
  32. package/example/NextJs/pages/_app.tsx +6 -0
  33. package/example/NextJs/pages/index.tsx +182 -0
  34. package/example/NextJs/public/favicon.ico +0 -0
  35. package/example/NextJs/public/vercel.svg +4 -0
  36. package/example/NextJs/server.js +40 -0
  37. package/example/NextJs/styles/Chat.module.css +194 -0
  38. package/example/NextJs/styles/Home.module.css +129 -0
  39. package/example/NextJs/styles/globals.css +26 -0
  40. package/example/NextJs/tsconfig.json +20 -0
  41. package/example/README.md +66 -0
  42. package/index.d.ts +179 -0
  43. package/index.js +11 -0
  44. package/package.json +11 -4
  45. package/server/README.md +93 -0
  46. package/server/index.js +6 -0
  47. package/server/lib/broadcast.js +63 -0
  48. package/server/lib/loader.js +10 -0
  49. package/server/lib/main.js +23 -0
  50. package/server/lib/wiring.js +94 -0
  51. package/server/security/extractRootDomain.js +21 -0
  52. package/server/security/origin.js +13 -0
  53. package/server/security/reply.js +21 -0
  54. package/server/socket/open.js +10 -0
  55. package/server/socket/receive.js +66 -0
  56. package/server/socket/send.js +55 -0
  57. package/server/utils/deepRequire.js +45 -0
  58. package/server/utils/genId.js +24 -0
  59. package/todo.md +85 -0
  60. package/utils/jss.js +273 -0
  61. package/utils/jss.test.js +261 -0
  62. package/utils/messageHash.js +43 -0
  63. package/utils/messageHash.test.js +56 -0
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "apiape_example",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "node server.js",
7
+ "build": "next build",
8
+ "start": "node server.js",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "@types/node": "18.11.13",
13
+ "@types/react": "18.0.26",
14
+ "@types/react-dom": "18.0.9",
15
+ "api-ape": "file:../..",
16
+ "eslint": "8.29.0",
17
+ "eslint-config-next": "13.0.6",
18
+ "express": "^4.18.2",
19
+ "next": "13.0.6",
20
+ "react": "18.2.0",
21
+ "react-dom": "18.2.0",
22
+ "typescript": "4.9.4",
23
+ "ws": "^8.14.0"
24
+ }
25
+ }
@@ -0,0 +1,6 @@
1
+ import '../styles/globals.css'
2
+ import type { AppProps } from 'next/app'
3
+
4
+ export default function App({ Component, pageProps }: AppProps) {
5
+ return <Component {...pageProps} />
6
+ }
@@ -0,0 +1,182 @@
1
+ import Head from 'next/head'
2
+ import { useState, useEffect, useRef } from 'react'
3
+ import styles from '../styles/Chat.module.css'
4
+ import { getApeClient } from '../ape/client'
5
+
6
+ export default function Home() {
7
+ const [messages, setMessages] = useState([])
8
+ const [input, setInput] = useState('')
9
+ const [username, setUsername] = useState('')
10
+ const [joined, setJoined] = useState(false)
11
+ const [userCount, setUserCount] = useState(0)
12
+ const [sending, setSending] = useState(false)
13
+ const [connected, setConnected] = useState(false)
14
+ const messagesEndRef = useRef(null)
15
+ const apiRef = useRef(null) // The sender proxy
16
+
17
+ // Initialize api-ape client on mount (before join)
18
+ useEffect(() => {
19
+ if (typeof window === 'undefined') return
20
+
21
+ getApeClient().then((client) => {
22
+ if (!client) return
23
+
24
+ // Store the sender proxy - ready to use!
25
+ apiRef.current = client.sender
26
+ setConnected(true)
27
+ console.log('🦍 api-ape ready')
28
+
29
+ // Set up message listeners
30
+ client.setOnReciver('init', ({ data }) => {
31
+ setMessages(data.history || [])
32
+ setUserCount(data.users || 0)
33
+ console.log('🦍 Initialized')
34
+ })
35
+
36
+ client.setOnReciver('message', ({ data }) => {
37
+ setMessages(prev => [...prev, data.message])
38
+ })
39
+
40
+ client.setOnReciver('users', ({ data }) => {
41
+ setUserCount(data.count)
42
+ })
43
+ })
44
+ }, [])
45
+
46
+ // Auto-scroll to bottom
47
+ useEffect(() => {
48
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
49
+ }, [messages])
50
+
51
+ // Send message using the proxy pattern
52
+ // api.message(data) -> sends type="message" with data
53
+ // Returns a Promise - server can reply with matching queryId
54
+ const sendMessage = (e) => {
55
+ e.preventDefault()
56
+ if (!input.trim() || !apiRef.current || sending) return
57
+
58
+ const api = apiRef.current
59
+
60
+ setSending(true)
61
+
62
+ // api.message({ user, text }) - "message" is the type!
63
+ // createdAt is auto-added by client
64
+ // queryId hash is auto-generated for request/response matching
65
+ api.message({ user: username, text: input })
66
+ .then((response) => {
67
+ // Server replied with matching queryId
68
+ // Add our own message to the list (we sent it, so we add it)
69
+ if (response?.message) {
70
+ setMessages(prev => [...prev, response.message])
71
+ }
72
+ setSending(false)
73
+ })
74
+ .catch((err) => {
75
+ console.error('Send failed:', err)
76
+ setSending(false)
77
+ })
78
+
79
+ setInput('')
80
+ }
81
+
82
+ const handleJoin = (e) => {
83
+ e.preventDefault()
84
+ if (username.trim()) {
85
+ setJoined(true)
86
+ }
87
+ }
88
+
89
+ return (
90
+ <div className={styles.container}>
91
+ <Head>
92
+ <title>🦍 api-ape Chat</title>
93
+ <meta name="description" content="Real-time WebSocket chat using api-ape" />
94
+ </Head>
95
+
96
+ <main className={styles.main}>
97
+ <h1 className={styles.title}>
98
+ 🦍 <span className={styles.gradient}>api-ape</span> Chat
99
+ </h1>
100
+ <p className={styles.subtitle}>
101
+ {connected ? '✅ Connected' : '⏳ Connecting...'} • Pure WebSocket
102
+ </p>
103
+
104
+ {!joined ? (
105
+ <form onSubmit={handleJoin} className={styles.joinForm}>
106
+ <input
107
+ type="text"
108
+ placeholder="Enter your name..."
109
+ value={username}
110
+ onChange={(e) => setUsername(e.target.value)}
111
+ className={styles.input}
112
+ autoFocus
113
+ />
114
+ <button type="submit" className={styles.button} disabled={!connected}>
115
+ {connected ? 'Join Chat →' : 'Connecting...'}
116
+ </button>
117
+ </form>
118
+ ) : (
119
+ <div className={styles.chatContainer}>
120
+ <div className={styles.header}>
121
+ <span>💬 {username}</span>
122
+ <span className={styles.userCount}>
123
+ 🟢 {userCount} online
124
+ </span>
125
+ </div>
126
+
127
+ <div className={styles.messages}>
128
+ {messages.length === 0 && (
129
+ <p className={styles.emptyState}>No messages yet. Say hi! 👋</p>
130
+ )}
131
+ {messages.map((msg, i) => (
132
+ <div
133
+ key={i}
134
+ className={`${styles.message} ${msg.user === username ? styles.myMessage : ''}`}
135
+ >
136
+ <strong className={styles.username}>{msg.user}</strong>
137
+ <span>{msg.text}</span>
138
+ <span className={styles.time}>
139
+ {new Date(msg.time).toLocaleTimeString()}
140
+ </span>
141
+ </div>
142
+ ))}
143
+ <div ref={messagesEndRef} />
144
+ </div>
145
+
146
+ <form onSubmit={sendMessage} className={styles.inputForm}>
147
+ <input
148
+ type="text"
149
+ placeholder="Type a message..."
150
+ value={input}
151
+ onChange={(e) => setInput(e.target.value)}
152
+ className={styles.messageInput}
153
+ disabled={sending}
154
+ autoFocus
155
+ />
156
+ <button type="submit" className={styles.sendButton} disabled={sending}>
157
+ {sending ? '...' : 'Send'}
158
+ </button>
159
+ </form>
160
+ </div>
161
+ )}
162
+
163
+ <div className={styles.codeSection}>
164
+ <h3 className={styles.codeTitle}>✨ api-ape Proxy Pattern</h3>
165
+ <pre className={styles.code}>
166
+ {`// Sender is a Proxy - prop name = type
167
+ const api = client.sender
168
+
169
+ // Send with Promise - queryId auto-matched
170
+ setSending(true)
171
+ api.message({ user, text })
172
+ .then(() => setSending(false))
173
+ .catch(err => console.error(err))
174
+
175
+ // createdAt auto-added by client
176
+ // queryId hash matches request/response`}
177
+ </pre>
178
+ </div>
179
+ </main>
180
+ </div>
181
+ )
182
+ }
@@ -0,0 +1,4 @@
1
+ <svg width="283" height="64" viewBox="0 0 283 64" fill="none"
2
+ xmlns="http://www.w3.org/2000/svg">
3
+ <path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
4
+ </svg>
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Custom Next.js server using actual api-ape library with Express
3
+ */
4
+
5
+ const express = require('express')
6
+ const next = require('next')
7
+ const ape = require('api-ape')
8
+ const { onConnect } = require('./ape/onConnect')
9
+
10
+ const dev = process.env.NODE_ENV !== 'production'
11
+ const hostname = '0.0.0.0'
12
+ const port = parseInt(process.env.PORT, 10) || 3000
13
+
14
+ const app = next({ dev, hostname, port })
15
+ const handle = app.getRequestHandler()
16
+
17
+ app.prepare().then(() => {
18
+ const server = express()
19
+
20
+ // Initialize api-ape - it handles EVERYTHING
21
+ // Developer never touches WebSocket!
22
+ ape(server, { where: 'api', onConnent: onConnect })
23
+
24
+ // Let Next.js handle all other routes
25
+ server.all('*', (req, res) => {
26
+ return handle(req, res)
27
+ })
28
+
29
+ server.listen(port, () => {
30
+ console.log(`
31
+ ╔═══════════════════════════════════════════════════════╗
32
+ ║ 🦍 api-ape NextJS Demo ║
33
+ ╠═══════════════════════════════════════════════════════╣
34
+ ║ HTTP: http://localhost:${port}/ ║
35
+ ║ WebSocket: ws://localhost:${port}/api/ape ║
36
+ ║ ape(app, { where: "api", onConnent }) ║
37
+ ╚═══════════════════════════════════════════════════════╝
38
+ `)
39
+ })
40
+ })
@@ -0,0 +1,194 @@
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: 600px;
10
+ margin: 0 auto;
11
+ padding: 2rem;
12
+ }
13
+
14
+ .title {
15
+ font-size: 2.5rem;
16
+ text-align: center;
17
+ margin-bottom: 0.5rem;
18
+ }
19
+
20
+ .gradient {
21
+ background: linear-gradient(90deg, #00d2ff, #3a7bd5);
22
+ -webkit-background-clip: text;
23
+ -webkit-text-fill-color: transparent;
24
+ background-clip: text;
25
+ }
26
+
27
+ .subtitle {
28
+ text-align: center;
29
+ color: #0f0;
30
+ margin-bottom: 2rem;
31
+ font-weight: bold;
32
+ }
33
+
34
+ .joinForm {
35
+ display: flex;
36
+ gap: 1rem;
37
+ justify-content: center;
38
+ }
39
+
40
+ .input {
41
+ padding: 1rem 1.5rem;
42
+ font-size: 1rem;
43
+ border: none;
44
+ border-radius: 50px;
45
+ background: rgba(255, 255, 255, 0.1);
46
+ color: #fff;
47
+ outline: none;
48
+ width: 250px;
49
+ }
50
+
51
+ .input::placeholder {
52
+ color: rgba(255, 255, 255, 0.5);
53
+ }
54
+
55
+ .button {
56
+ padding: 1rem 2rem;
57
+ font-size: 1rem;
58
+ border: none;
59
+ border-radius: 50px;
60
+ background: linear-gradient(90deg, #00d2ff, #3a7bd5);
61
+ color: #fff;
62
+ cursor: pointer;
63
+ font-weight: bold;
64
+ }
65
+
66
+ .button:hover {
67
+ opacity: 0.9;
68
+ }
69
+
70
+ .chatContainer {
71
+ background: rgba(255, 255, 255, 0.05);
72
+ border-radius: 20px;
73
+ overflow: hidden;
74
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
75
+ }
76
+
77
+ .header {
78
+ display: flex;
79
+ justify-content: space-between;
80
+ padding: 1rem 1.5rem;
81
+ background: rgba(255, 255, 255, 0.1);
82
+ font-weight: bold;
83
+ }
84
+
85
+ .userCount {
86
+ font-size: 0.85rem;
87
+ color: #0f0;
88
+ }
89
+
90
+ .messages {
91
+ height: 300px;
92
+ overflow-y: auto;
93
+ padding: 1rem;
94
+ }
95
+
96
+ .emptyState {
97
+ text-align: center;
98
+ color: #666;
99
+ margin-top: 120px;
100
+ }
101
+
102
+ .message {
103
+ display: flex;
104
+ flex-direction: column;
105
+ gap: 0.25rem;
106
+ padding: 0.75rem 1rem;
107
+ margin-bottom: 0.5rem;
108
+ background: rgba(255, 255, 255, 0.05);
109
+ border-radius: 12px;
110
+ border-left: 3px solid #3a7bd5;
111
+ }
112
+
113
+ .myMessage {
114
+ background: rgba(0, 210, 255, 0.15);
115
+ border-left: 3px solid #00d2ff;
116
+ }
117
+
118
+ .username {
119
+ color: #00d2ff;
120
+ font-size: 0.85rem;
121
+ }
122
+
123
+ .time {
124
+ color: #666;
125
+ font-size: 0.7rem;
126
+ }
127
+
128
+ .inputForm {
129
+ display: flex;
130
+ gap: 0.5rem;
131
+ padding: 1rem;
132
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
133
+ }
134
+
135
+ .messageInput {
136
+ flex: 1;
137
+ padding: 0.75rem 1rem;
138
+ font-size: 1rem;
139
+ border: none;
140
+ border-radius: 50px;
141
+ background: rgba(255, 255, 255, 0.1);
142
+ color: #fff;
143
+ outline: none;
144
+ }
145
+
146
+ .messageInput::placeholder {
147
+ color: rgba(255, 255, 255, 0.5);
148
+ }
149
+
150
+ .sendButton {
151
+ padding: 0.75rem 1.5rem;
152
+ font-size: 1rem;
153
+ border: none;
154
+ border-radius: 50px;
155
+ background: #3a7bd5;
156
+ color: #fff;
157
+ cursor: pointer;
158
+ font-weight: bold;
159
+ }
160
+
161
+ .sendButton:hover {
162
+ opacity: 0.9;
163
+ }
164
+
165
+ .codeSection {
166
+ margin-top: 2rem;
167
+ }
168
+
169
+ .codeTitle {
170
+ margin-bottom: 0.5rem;
171
+ color: #0f0;
172
+ }
173
+
174
+ .code {
175
+ background: rgba(0, 0, 0, 0.4);
176
+ padding: 1.5rem;
177
+ border-radius: 12px;
178
+ font-size: 0.75rem;
179
+ color: #0f0;
180
+ overflow: auto;
181
+ }
182
+
183
+ .clientInfo {
184
+ text-align: center;
185
+ margin-top: 1rem;
186
+ font-size: 0.85rem;
187
+ color: #888;
188
+ }
189
+
190
+ .clientInfo code {
191
+ background: rgba(0, 0, 0, 0.3);
192
+ padding: 0.2rem 0.5rem;
193
+ border-radius: 4px;
194
+ }
@@ -0,0 +1,129 @@
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
+ }
@@ -0,0 +1,26 @@
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
+ }
@@ -0,0 +1,20 @@
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
+ }
@@ -0,0 +1,66 @@
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
+
12
+ ---
13
+
14
+ ## ExpressJs — Basic Example
15
+
16
+ A minimal real-time chat demonstrating core api-ape concepts.
17
+
18
+ **Features:**
19
+ - Simple Express.js server with api-ape
20
+ - Broadcast messages to other clients
21
+ - Message history
22
+
23
+ **Quick Start:**
24
+ ```bash
25
+ cd ExpressJs
26
+ npm install
27
+ npm start
28
+ ```
29
+
30
+ **Key Files:**
31
+ - `backend.js` — Server setup (22 lines)
32
+ - `api/message.js` — Message handler with `this.broadcastOthers()`
33
+ - `index.html` — Browser client using `window.ape`
34
+
35
+ ---
36
+
37
+ ## NextJs — Complete Example
38
+
39
+ A production-ready chat application with Next.js integration.
40
+
41
+ **Features:**
42
+ - Custom Next.js server with api-ape
43
+ - React hooks integration
44
+ - User presence tracking
45
+ - Docker support
46
+ - Connection lifecycle hooks
47
+
48
+ **Quick Start:**
49
+ ```bash
50
+ cd NextJs
51
+ npm install
52
+ npm run dev
53
+ ```
54
+
55
+ **Or with Docker:**
56
+ ```bash
57
+ cd NextJs
58
+ docker-compose up --build
59
+ ```
60
+
61
+ **Key Files:**
62
+ - `server.js` — Custom Next.js server with api-ape
63
+ - `api/message.js` — Message controller with validation
64
+ - `ape/client.js` — React client wrapper
65
+ - `ape/onConnect.js` — Connection lifecycle hooks
66
+ - `pages/index.tsx` — Chat UI with React hooks