api-ape 0.0.0 → 1.0.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.
- package/README.md +458 -0
- package/client/README.md +69 -0
- package/client/browser.js +17 -0
- package/client/connectSocket.js +260 -0
- package/dist/ape.js +454 -0
- package/example/ExpressJs/README.md +97 -0
- package/example/ExpressJs/api/message.js +11 -0
- package/example/ExpressJs/backend.js +37 -0
- package/example/ExpressJs/index.html +88 -0
- package/example/ExpressJs/package-lock.json +834 -0
- package/example/ExpressJs/package.json +10 -0
- package/example/ExpressJs/styles.css +128 -0
- package/example/NextJs/.dockerignore +29 -0
- package/example/NextJs/Dockerfile +52 -0
- package/example/NextJs/Dockerfile.dev +27 -0
- package/example/NextJs/README.md +113 -0
- package/example/NextJs/ape/client.js +66 -0
- package/example/NextJs/ape/embed.js +12 -0
- package/example/NextJs/ape/index.js +23 -0
- package/example/NextJs/ape/logic/chat.js +62 -0
- package/example/NextJs/ape/onConnect.js +69 -0
- package/example/NextJs/ape/onDisconnect.js +13 -0
- package/example/NextJs/ape/onError.js +9 -0
- package/example/NextJs/ape/onReceive.js +15 -0
- package/example/NextJs/ape/onSend.js +15 -0
- package/example/NextJs/api/message.js +44 -0
- package/example/NextJs/docker-compose.yml +22 -0
- package/example/NextJs/next-env.d.ts +5 -0
- package/example/NextJs/next.config.js +8 -0
- package/example/NextJs/package-lock.json +5107 -0
- package/example/NextJs/package.json +25 -0
- package/example/NextJs/pages/Info.tsx +153 -0
- package/example/NextJs/pages/_app.tsx +6 -0
- package/example/NextJs/pages/index.tsx +264 -0
- package/example/NextJs/public/favicon.ico +0 -0
- package/example/NextJs/public/vercel.svg +4 -0
- package/example/NextJs/server.js +40 -0
- package/example/NextJs/styles/Chat.module.css +448 -0
- package/example/NextJs/styles/Home.module.css +129 -0
- package/example/NextJs/styles/globals.css +26 -0
- package/example/NextJs/tsconfig.json +20 -0
- package/example/README.md +66 -0
- package/index.d.ts +179 -0
- package/index.js +11 -0
- package/package.json +11 -4
- package/server/README.md +93 -0
- package/server/index.js +6 -0
- package/server/lib/broadcast.js +63 -0
- package/server/lib/loader.js +10 -0
- package/server/lib/main.js +23 -0
- package/server/lib/wiring.js +94 -0
- package/server/security/extractRootDomain.js +21 -0
- package/server/security/origin.js +13 -0
- package/server/security/reply.js +21 -0
- package/server/socket/open.js +10 -0
- package/server/socket/receive.js +66 -0
- package/server/socket/send.js +55 -0
- package/server/utils/deepRequire.js +45 -0
- package/server/utils/genId.js +24 -0
- package/todo.md +85 -0
- package/utils/jss.js +273 -0
- package/utils/jss.test.js +261 -0
- package/utils/messageHash.js +43 -0
- package/utils/messageHash.test.js +56 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# 🦍 ExpressJs — Basic Example
|
|
2
|
+
|
|
3
|
+
A minimal real-time chat app demonstrating api-ape core concepts.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
npm start
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Open http://localhost:3000 in multiple browser windows.
|
|
13
|
+
|
|
14
|
+
## Project Structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
ExpressJs/
|
|
18
|
+
├── backend.js # Express server with api-ape + onConnect hook
|
|
19
|
+
├── api/
|
|
20
|
+
│ └── message.js # Broadcast to other clients
|
|
21
|
+
├── index.html # Chat UI
|
|
22
|
+
└── styles.css # Styling
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## How It Works
|
|
26
|
+
|
|
27
|
+
### Server (backend.js)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm i api-ape
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
const express = require('express')
|
|
35
|
+
const ape = require('api-ape')
|
|
36
|
+
const { online, broadcast } = require('api-ape/server/lib/broadcast')
|
|
37
|
+
|
|
38
|
+
const app = express()
|
|
39
|
+
|
|
40
|
+
ape(app, {
|
|
41
|
+
where: 'api',
|
|
42
|
+
onConnent: (socket, req, send) => {
|
|
43
|
+
// Push history + user count on connect
|
|
44
|
+
const { _messages } = require('./api/message')
|
|
45
|
+
send('init', { history: _messages, users: online() })
|
|
46
|
+
broadcast('users', { count: online() })
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
onDisconnent: () => broadcast('users', { count: online() })
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
app.listen(3000)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Controller (api/message.js)
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
module.exports = function (data) {
|
|
61
|
+
this.broadcastOthers('message', data) // Send to other clients
|
|
62
|
+
return data // Reply to sender
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Client (index.html)
|
|
67
|
+
|
|
68
|
+
```html
|
|
69
|
+
<script src="/api/ape.js"></script>
|
|
70
|
+
<script>
|
|
71
|
+
// Receive init on connect (pushed by server)
|
|
72
|
+
ape.on('init', ({ data }) => {
|
|
73
|
+
console.log('History:', data.history)
|
|
74
|
+
console.log('Users online:', data.users)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Listen for user count updates
|
|
78
|
+
ape.on('users', ({ data }) => console.log('Online:', data.count))
|
|
79
|
+
|
|
80
|
+
// Listen for messages
|
|
81
|
+
ape.on('message', ({ data }) => console.log(data))
|
|
82
|
+
|
|
83
|
+
// Send message
|
|
84
|
+
ape.message({ text: 'Hello!' })
|
|
85
|
+
</script>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Key Concepts Demonstrated
|
|
89
|
+
|
|
90
|
+
| Concept | Example |
|
|
91
|
+
|---------|---------|
|
|
92
|
+
| Auto-wiring | `ape(app, { where: 'api' })` loads `api/*.js` |
|
|
93
|
+
| onConnect hook | `onConnent: (socket, req, send) => { ... }` |
|
|
94
|
+
| Push on connect | `send('init', { history, users })` |
|
|
95
|
+
| Broadcast all | `broadcast('users', { count })` |
|
|
96
|
+
| Broadcast others | `this.broadcastOthers('message', data)` |
|
|
97
|
+
| Listen | `ape.on('init', handler)` |
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const express = require('express')
|
|
2
|
+
const scribbles = require('scribbles')
|
|
3
|
+
const net = require('net')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const ape = require('api-ape')
|
|
6
|
+
|
|
7
|
+
const app = express()
|
|
8
|
+
|
|
9
|
+
const { online, broadcast } = require('api-ape/server/lib/broadcast')
|
|
10
|
+
|
|
11
|
+
ape(app, {
|
|
12
|
+
where: 'api',
|
|
13
|
+
onConnent: (socket, req, send) => {
|
|
14
|
+
// Send history + user count on connect
|
|
15
|
+
const { _messages } = require('./api/message')
|
|
16
|
+
setTimeout(() => {
|
|
17
|
+
send('init', { history: _messages, users: online() })
|
|
18
|
+
broadcast('users', { count: online() })
|
|
19
|
+
}, 100)
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
onDisconnent: () => broadcast('users', { count: online() })
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
app.get('/', (req, res) => res.sendFile(path.join(__dirname, 'index.html')))
|
|
28
|
+
app.get('/styles.css', (req, res) => res.sendFile(path.join(__dirname, 'styles.css')))
|
|
29
|
+
|
|
30
|
+
const findPort = (port, cb) => {
|
|
31
|
+
const server = net.createServer()
|
|
32
|
+
server.once('error', () => findPort(port + 1, cb))
|
|
33
|
+
server.once('listening', () => server.close(() => cb(port)))
|
|
34
|
+
server.listen(port)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
findPort(3000, port => app.listen(port, () => scribbles.log(`http://localhost:${port}`)))
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<title>Chat - api-ape</title>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
|
+
<link rel="stylesheet" href="/styles.css">
|
|
8
|
+
</head>
|
|
9
|
+
|
|
10
|
+
<body>
|
|
11
|
+
<chat-app></chat-app>
|
|
12
|
+
<script src="https://unpkg.com/hyperhtml"></script>
|
|
13
|
+
<script src="https://unpkg.com/hyper-element"></script>
|
|
14
|
+
<script src="/api/ape.js"></script>
|
|
15
|
+
<script>
|
|
16
|
+
customElements.define('chat-app', class extends hyperElement {
|
|
17
|
+
setup(attachStore) {
|
|
18
|
+
this.user = 'User' + Math.random().toString(36).slice(2, 6)
|
|
19
|
+
this.messages = []
|
|
20
|
+
|
|
21
|
+
this.onStoreChange = attachStore(() => this.messages)
|
|
22
|
+
|
|
23
|
+
// Receive init with history + user count on connect
|
|
24
|
+
ape.on('init', ({ data }) => {
|
|
25
|
+
this.messages = data.history || []
|
|
26
|
+
this.users = data.users || 0
|
|
27
|
+
this.onStoreChange()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// Listen for user count updates
|
|
31
|
+
ape.on('users', ({ data }) => {
|
|
32
|
+
this.users = data.count
|
|
33
|
+
this.onStoreChange()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// Listen for new messages from others
|
|
37
|
+
ape.on('message', ({ data }) => {
|
|
38
|
+
this.messages.push(data)
|
|
39
|
+
this.onStoreChange()
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
send(e) {
|
|
44
|
+
if (e.key !== 'Enter' || !e.target.value) return
|
|
45
|
+
const msg = { user: this.user, text: e.target.value }
|
|
46
|
+
ape.message(msg).then(() => {
|
|
47
|
+
this.messages.push(msg)
|
|
48
|
+
this.onStoreChange()
|
|
49
|
+
})
|
|
50
|
+
e.target.value = ''
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
render(Html, messages) {
|
|
54
|
+
messages = messages || []
|
|
55
|
+
Html`
|
|
56
|
+
<div class="chat-container">
|
|
57
|
+
<div class="header">
|
|
58
|
+
<span class="title">💬 Chat</span>
|
|
59
|
+
<span class="online-count">🟢 ${this.users || 0} online</span>
|
|
60
|
+
<span class="user-badge">● ${this.user}</span>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="messages">
|
|
63
|
+
${messages.length === 0
|
|
64
|
+
? Html.wire()`<div class="empty-state">No messages yet...</div>`
|
|
65
|
+
: messages.map(m => Html.wire(m, ':msg')`
|
|
66
|
+
<div class="${'message' + (m.user === this.user ? ' mine' : '')}">
|
|
67
|
+
<span class="username">${m.user}</span>
|
|
68
|
+
<span class="text">${m.text}</span>
|
|
69
|
+
</div>
|
|
70
|
+
`)
|
|
71
|
+
}
|
|
72
|
+
</div>
|
|
73
|
+
<div class="input-area">
|
|
74
|
+
<input
|
|
75
|
+
class="message-input"
|
|
76
|
+
placeholder="Type a message..."
|
|
77
|
+
onkeydown=${e => this.send(e)}
|
|
78
|
+
autofocus
|
|
79
|
+
>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
`
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
</script>
|
|
86
|
+
</body>
|
|
87
|
+
|
|
88
|
+
</html>
|