api-ape 2.3.0 → 3.0.0
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 +4 -3
- package/index.d.ts +46 -2
- package/index.js +15 -0
- package/package.json +1 -1
- package/server/README.md +30 -4
- package/server/client.js +299 -0
- package/server/index.js +26 -3
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/api-ape)
|
|
4
4
|
[](https://github.com/codemeasandwich/api-ape/issues)
|
|
5
5
|
[](#zero-dependencies)
|
|
6
|
+
[](https://github.com/codemeasandwich/api-ape/security/dependabot)
|
|
6
7
|
[](#csrf-protection)
|
|
7
8
|
[](https://bundlephobia.com/package/api-ape)
|
|
8
9
|
[](#jjs-encoding)
|
|
@@ -32,7 +33,7 @@ yarn add api-ape
|
|
|
32
33
|
|
|
33
34
|
```js
|
|
34
35
|
const { createServer } = require('http')
|
|
35
|
-
const ape = require('api-ape')
|
|
36
|
+
const { ape } = require('api-ape')
|
|
36
37
|
|
|
37
38
|
const server = createServer()
|
|
38
39
|
|
|
@@ -45,7 +46,7 @@ server.listen(3000)
|
|
|
45
46
|
**With Express:**
|
|
46
47
|
```js
|
|
47
48
|
const express = require('express')
|
|
48
|
-
const ape = require('api-ape')
|
|
49
|
+
const { ape } = require('api-ape')
|
|
49
50
|
|
|
50
51
|
const app = express()
|
|
51
52
|
const server = app.listen(3000) // Get the HTTP server
|
|
@@ -344,7 +345,7 @@ module.exports = function(announcement) {
|
|
|
344
345
|
### Using ape.clients
|
|
345
346
|
|
|
346
347
|
```js
|
|
347
|
-
const ape = require('api-ape')
|
|
348
|
+
const { ape } = require('api-ape')
|
|
348
349
|
|
|
349
350
|
// Get online count
|
|
350
351
|
console.log('Online:', ape.clients.size)
|
package/index.d.ts
CHANGED
|
@@ -219,6 +219,10 @@ export interface ForestOptions {
|
|
|
219
219
|
|
|
220
220
|
/**
|
|
221
221
|
* Initialize api-ape on a Node.js HTTP/HTTPS server
|
|
222
|
+
*
|
|
223
|
+
* V3 Breaking Change:
|
|
224
|
+
* Old: const ape = require('api-ape')
|
|
225
|
+
* New: const { ape } = require('api-ape')
|
|
222
226
|
*/
|
|
223
227
|
declare function ape(server: HttpServer, options: ApeServerOptions): void
|
|
224
228
|
|
|
@@ -280,8 +284,48 @@ declare namespace ape {
|
|
|
280
284
|
export function message<T = any, R = any>(data?: T): Promise<R>
|
|
281
285
|
}
|
|
282
286
|
|
|
283
|
-
//
|
|
284
|
-
|
|
287
|
+
// =============================================================================
|
|
288
|
+
// SERVER-SIDE CLIENT (api - same interface as browser)
|
|
289
|
+
// =============================================================================
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Server-side client for connecting to other api-ape servers
|
|
293
|
+
* 100% identical interface to the browser client
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* import api from 'api-ape'
|
|
297
|
+
*
|
|
298
|
+
* // Configure connection (or set APE_SERVER env)
|
|
299
|
+
* api.connect('ws://other-server:3000/api/ape')
|
|
300
|
+
*
|
|
301
|
+
* // Same usage as browser
|
|
302
|
+
* const result = await api.hello('World')
|
|
303
|
+
* api.on('message', (data) => console.log(data))
|
|
304
|
+
*/
|
|
305
|
+
export interface ApeServerClient extends ApeSender {
|
|
306
|
+
/** Subscribe to broadcasts from the remote server */
|
|
307
|
+
on<T = any>(type: string, handler: MessageHandler<T>): void
|
|
308
|
+
on(handler: MessageHandler): void
|
|
309
|
+
/** Subscribe to connection state changes */
|
|
310
|
+
onConnectionChange(handler: (state: ConnectionState) => void): () => void
|
|
311
|
+
/** Connect to a server (or set APE_SERVER env) */
|
|
312
|
+
connect(url: string): void
|
|
313
|
+
/** Close the connection */
|
|
314
|
+
close(): void
|
|
315
|
+
/** Current transport type (read-only) */
|
|
316
|
+
readonly transport: 'websocket' | null
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* The api client - works identically on browser and server
|
|
321
|
+
*/
|
|
322
|
+
declare const api: ApeServerClient
|
|
323
|
+
|
|
324
|
+
// Named export for V3 (Server usage: const { ape } = require('api-ape'))
|
|
325
|
+
export { ape, api }
|
|
326
|
+
|
|
327
|
+
// Default export: api client (same interface on browser and server)
|
|
328
|
+
export default api
|
|
285
329
|
|
|
286
330
|
// =============================================================================
|
|
287
331
|
// BROWSER CLIENT (Default export in browser context)
|
package/index.js
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* api-ape unified entry point
|
|
3
|
+
*
|
|
4
|
+
* V3 Server Usage:
|
|
5
|
+
* const api = require('api-ape') // Client factory (default)
|
|
6
|
+
* const { ape } = require('api-ape') // Server initializer (named)
|
|
7
|
+
* import api, { ape } from 'api-ape' // ESM both
|
|
8
|
+
*
|
|
9
|
+
* Browser Usage:
|
|
10
|
+
* import api from 'api-ape' // Auto-connecting client
|
|
11
|
+
*/
|
|
1
12
|
|
|
2
13
|
let apiApe;
|
|
3
14
|
|
|
4
15
|
if ('undefined' === typeof window
|
|
5
16
|
|| 'undefined' === typeof window.document) {
|
|
17
|
+
// Server environment - exports: api (default), ape, broadcast, clients, createClient
|
|
6
18
|
apiApe = require('./server');
|
|
7
19
|
} else {
|
|
20
|
+
// Browser environment - client module has its own exports
|
|
8
21
|
apiApe = require('./client');
|
|
9
22
|
}
|
|
10
23
|
|
|
11
24
|
module.exports = apiApe
|
|
25
|
+
|
|
26
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "api-ape",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Remote Procedure Events (RPE) - A lightweight WebSocket framework for building real-time APIs. Call server functions from the browser like local methods with automatic reconnection, HTTP streaming fallback, and extended JSON encoding.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"browser": "./client/index.js",
|
package/server/README.md
CHANGED
|
@@ -49,7 +49,7 @@ npm i api-ape
|
|
|
49
49
|
|
|
50
50
|
```js
|
|
51
51
|
const { createServer } = require('http')
|
|
52
|
-
const ape = require('api-ape')
|
|
52
|
+
const { ape } = require('api-ape')
|
|
53
53
|
|
|
54
54
|
const server = createServer()
|
|
55
55
|
|
|
@@ -64,6 +64,32 @@ ape(server, {
|
|
|
64
64
|
server.listen(3000)
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
## Server-to-Server Connection
|
|
68
|
+
|
|
69
|
+
Your server can connect to **another** api-ape server as a client. The API is 100% identical to browser usage:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
import api, { ape } from 'api-ape'
|
|
73
|
+
|
|
74
|
+
// Start your own server
|
|
75
|
+
ape(server, { where: 'api' })
|
|
76
|
+
|
|
77
|
+
// Connect to another api-ape server
|
|
78
|
+
api.connect('ws://other-server:3000/api/ape')
|
|
79
|
+
|
|
80
|
+
// Now use it exactly like browser code!
|
|
81
|
+
const result = await api.hello('World')
|
|
82
|
+
api.on('message', ({ data }) => console.log(data))
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Or set the connection URL via environment variable:**
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
APE_SERVER=ws://other-server:3000/api/ape node app.js
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This enables server-side microservice patterns while keeping the familiar api-ape interface.
|
|
92
|
+
|
|
67
93
|
## API
|
|
68
94
|
|
|
69
95
|
### `ape(server, options)`
|
|
@@ -273,7 +299,7 @@ The built-in polyfill implements:
|
|
|
273
299
|
### Quick Start
|
|
274
300
|
|
|
275
301
|
```js
|
|
276
|
-
const ape = require('api-ape');
|
|
302
|
+
const { ape } = require('api-ape');
|
|
277
303
|
const { createClient } = require('redis');
|
|
278
304
|
|
|
279
305
|
const redis = createClient();
|
|
@@ -453,7 +479,7 @@ ape.joinVia({
|
|
|
453
479
|
|
|
454
480
|
**Server A (port 3001):**
|
|
455
481
|
```js
|
|
456
|
-
const ape = require('api-ape');
|
|
482
|
+
const { ape } = require('api-ape');
|
|
457
483
|
const redis = createClient();
|
|
458
484
|
await redis.connect();
|
|
459
485
|
|
|
@@ -465,7 +491,7 @@ server.listen(3001);
|
|
|
465
491
|
|
|
466
492
|
**Server B (port 3002):**
|
|
467
493
|
```js
|
|
468
|
-
const ape = require('api-ape');
|
|
494
|
+
const { ape } = require('api-ape');
|
|
469
495
|
const redis = createClient();
|
|
470
496
|
await redis.connect();
|
|
471
497
|
|
package/server/client.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* api-ape Node.js client
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the browser client API exactly - same usage on server and browser.
|
|
5
|
+
*
|
|
6
|
+
* Usage (identical to browser):
|
|
7
|
+
* import api from 'api-ape'
|
|
8
|
+
*
|
|
9
|
+
* api.message({ user: 'Bob', text: 'Hello!' })
|
|
10
|
+
* api.on('message', (data) => console.log(data))
|
|
11
|
+
* api.onConnectionChange((state) => console.log(state))
|
|
12
|
+
*
|
|
13
|
+
* Configuration:
|
|
14
|
+
* Set APE_SERVER environment variable to the WebSocket URL:
|
|
15
|
+
* APE_SERVER=ws://other-server:3000/api/ape node app.js
|
|
16
|
+
*
|
|
17
|
+
* Or call api.connect(url) before first use
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const jss = require('../utils/jss')
|
|
21
|
+
const { WebSocket: WsPolyfill } = require('./lib/ws')
|
|
22
|
+
|
|
23
|
+
// Use native WebSocket if available (Node 22+), otherwise use polyfill
|
|
24
|
+
const WebSocket = globalThis.WebSocket || WsPolyfill
|
|
25
|
+
|
|
26
|
+
// Connection state enum
|
|
27
|
+
const ConnectionState = {
|
|
28
|
+
Disconnected: 'disconnected',
|
|
29
|
+
Connecting: 'connecting',
|
|
30
|
+
Connected: 'connected',
|
|
31
|
+
Closing: 'closing'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Shared state (mirrors browser client)
|
|
35
|
+
let ws = null
|
|
36
|
+
let connectionState = ConnectionState.Disconnected
|
|
37
|
+
const connectionChangeListeners = []
|
|
38
|
+
const waitingOn = {}
|
|
39
|
+
const receiverArray = []
|
|
40
|
+
const ofTypesOb = {}
|
|
41
|
+
let queryCounter = 0
|
|
42
|
+
let bufferedCalls = []
|
|
43
|
+
let bufferedReceivers = []
|
|
44
|
+
let ready = false
|
|
45
|
+
let reconnectEnabled = true
|
|
46
|
+
let reconnectTimer = null
|
|
47
|
+
let serverUrl = process.env.APE_SERVER || null
|
|
48
|
+
|
|
49
|
+
const joinKey = '/'
|
|
50
|
+
const connectTimeout = 5000
|
|
51
|
+
const totalRequestTimeout = 10000
|
|
52
|
+
|
|
53
|
+
function notifyConnectionChange(newState) {
|
|
54
|
+
if (connectionState !== newState) {
|
|
55
|
+
connectionState = newState
|
|
56
|
+
connectionChangeListeners.forEach(fn => fn(newState))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function generateQueryId() {
|
|
61
|
+
return `q${Date.now().toString(36)}_${(queryCounter++).toString(36)}`
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function connect(url) {
|
|
65
|
+
if (url) serverUrl = url
|
|
66
|
+
|
|
67
|
+
if (!serverUrl) {
|
|
68
|
+
console.warn('🦍 api-ape: No server URL configured. Set APE_SERVER env or call api.connect(url)')
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (ws && ws.readyState !== WebSocket.CLOSED) {
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
notifyConnectionChange(ConnectionState.Connecting)
|
|
77
|
+
|
|
78
|
+
ws = new WebSocket(serverUrl)
|
|
79
|
+
|
|
80
|
+
ws.onopen = () => {
|
|
81
|
+
ready = true
|
|
82
|
+
notifyConnectionChange(ConnectionState.Connected)
|
|
83
|
+
|
|
84
|
+
// Flush buffered receivers
|
|
85
|
+
bufferedReceivers.forEach(({ type, handler }) => {
|
|
86
|
+
setOnReceiver(type, handler)
|
|
87
|
+
})
|
|
88
|
+
bufferedReceivers = []
|
|
89
|
+
|
|
90
|
+
// Flush buffered calls
|
|
91
|
+
bufferedCalls.forEach(({ type, data, resolve, reject, createdAt, timer }) => {
|
|
92
|
+
clearTimeout(timer)
|
|
93
|
+
send(type, data, createdAt).then(resolve).catch(reject)
|
|
94
|
+
})
|
|
95
|
+
bufferedCalls = []
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
ws.onmessage = (event) => {
|
|
99
|
+
const msg = jss.parse(typeof event.data === 'string' ? event.data : event.data.toString())
|
|
100
|
+
const { err, type, queryId, data } = msg
|
|
101
|
+
|
|
102
|
+
// Response to a query
|
|
103
|
+
if (queryId && waitingOn[queryId]) {
|
|
104
|
+
waitingOn[queryId](err, data)
|
|
105
|
+
delete waitingOn[queryId]
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Broadcast message
|
|
110
|
+
if (ofTypesOb[type]) {
|
|
111
|
+
ofTypesOb[type].forEach(handler => handler({ err, type, data }))
|
|
112
|
+
}
|
|
113
|
+
receiverArray.forEach(handler => handler({ err, type, data }))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
ws.onerror = (err) => {
|
|
117
|
+
console.error('🦍 api-ape client error:', err.message || err)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
ws.onclose = () => {
|
|
121
|
+
ready = false
|
|
122
|
+
ws = null
|
|
123
|
+
notifyConnectionChange(ConnectionState.Disconnected)
|
|
124
|
+
|
|
125
|
+
if (reconnectEnabled && serverUrl) {
|
|
126
|
+
reconnectTimer = setTimeout(() => connect(), 1000)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function send(type, data, createdAt = Date.now()) {
|
|
132
|
+
const queryId = generateQueryId()
|
|
133
|
+
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
const timer = setTimeout(() => {
|
|
136
|
+
delete waitingOn[queryId]
|
|
137
|
+
reject(new Error(`Request timeout: ${type}`))
|
|
138
|
+
}, totalRequestTimeout)
|
|
139
|
+
|
|
140
|
+
waitingOn[queryId] = (err, result) => {
|
|
141
|
+
clearTimeout(timer)
|
|
142
|
+
if (err) {
|
|
143
|
+
reject(typeof err === 'string' ? new Error(err) : err)
|
|
144
|
+
} else {
|
|
145
|
+
resolve(result)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const message = jss.stringify({ type, data, queryId, createdAt })
|
|
150
|
+
ws.send(message)
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function queueOrSend(type, data) {
|
|
155
|
+
if (ready && ws && ws.readyState === WebSocket.OPEN) {
|
|
156
|
+
return send(type, data)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Queue the message
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
const createdAt = Date.now()
|
|
162
|
+
const timer = setTimeout(() => {
|
|
163
|
+
const idx = bufferedCalls.findIndex(m => m.createdAt === createdAt)
|
|
164
|
+
if (idx > -1) bufferedCalls.splice(idx, 1)
|
|
165
|
+
reject(new Error(`Connection timeout: ${type}`))
|
|
166
|
+
}, connectTimeout)
|
|
167
|
+
|
|
168
|
+
bufferedCalls.push({ type, data, resolve, reject, createdAt, timer })
|
|
169
|
+
|
|
170
|
+
// Ensure we're connecting
|
|
171
|
+
if (connectionState === ConnectionState.Disconnected && serverUrl) {
|
|
172
|
+
connect()
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Subscribe to broadcasts from the server (same as browser api.on)
|
|
179
|
+
*/
|
|
180
|
+
function on(type, handler) {
|
|
181
|
+
if (typeof type === 'function') {
|
|
182
|
+
handler = type
|
|
183
|
+
type = null
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (ready) {
|
|
187
|
+
setOnReceiver(type, handler)
|
|
188
|
+
} else {
|
|
189
|
+
bufferedReceivers.push({ type, handler })
|
|
190
|
+
if (serverUrl) connect()
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function setOnReceiver(type, handler) {
|
|
195
|
+
if (type === null) {
|
|
196
|
+
receiverArray.push(handler)
|
|
197
|
+
} else {
|
|
198
|
+
if (!ofTypesOb[type]) ofTypesOb[type] = []
|
|
199
|
+
ofTypesOb[type].push(handler)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Subscribe to connection state changes (same as browser api.onConnectionChange)
|
|
205
|
+
*/
|
|
206
|
+
function onConnectionChange(handler) {
|
|
207
|
+
connectionChangeListeners.push(handler)
|
|
208
|
+
handler(connectionState)
|
|
209
|
+
return () => {
|
|
210
|
+
const idx = connectionChangeListeners.indexOf(handler)
|
|
211
|
+
if (idx > -1) connectionChangeListeners.splice(idx, 1)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Create the sender proxy (mirrors browser client exactly)
|
|
217
|
+
*/
|
|
218
|
+
const handler = {
|
|
219
|
+
get(target, prop) {
|
|
220
|
+
// Reserved properties - same as browser
|
|
221
|
+
if (prop === 'on') return on
|
|
222
|
+
if (prop === 'onConnectionChange') return onConnectionChange
|
|
223
|
+
if (prop === 'transport') return ready ? 'websocket' : null
|
|
224
|
+
if (prop === 'connect') return connect
|
|
225
|
+
if (prop === 'close') return close
|
|
226
|
+
if (prop === 'then' || prop === 'catch') return undefined // Not a Promise
|
|
227
|
+
|
|
228
|
+
// Return a function that either calls directly or buffers
|
|
229
|
+
const wrapperFn = function (a, b) {
|
|
230
|
+
let path = joinKey + prop, body
|
|
231
|
+
if (arguments.length === 2) {
|
|
232
|
+
path += a
|
|
233
|
+
body = b
|
|
234
|
+
} else {
|
|
235
|
+
body = a
|
|
236
|
+
}
|
|
237
|
+
return queueOrSend(path, body)
|
|
238
|
+
}
|
|
239
|
+
return new Proxy(wrapperFn, handler)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function close() {
|
|
244
|
+
reconnectEnabled = false
|
|
245
|
+
if (reconnectTimer) {
|
|
246
|
+
clearTimeout(reconnectTimer)
|
|
247
|
+
reconnectTimer = null
|
|
248
|
+
}
|
|
249
|
+
if (ws) {
|
|
250
|
+
notifyConnectionChange(ConnectionState.Closing)
|
|
251
|
+
ws.close()
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Create the proxy (same interface as browser senderProxy)
|
|
256
|
+
const api = new Proxy({}, handler)
|
|
257
|
+
|
|
258
|
+
// Define properties on the proxy (same as browser)
|
|
259
|
+
Object.defineProperty(api, 'on', {
|
|
260
|
+
value: on,
|
|
261
|
+
writable: false,
|
|
262
|
+
enumerable: false,
|
|
263
|
+
configurable: false
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
Object.defineProperty(api, 'onConnectionChange', {
|
|
267
|
+
value: onConnectionChange,
|
|
268
|
+
writable: false,
|
|
269
|
+
enumerable: false,
|
|
270
|
+
configurable: false
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
Object.defineProperty(api, 'connect', {
|
|
274
|
+
value: connect,
|
|
275
|
+
writable: false,
|
|
276
|
+
enumerable: false,
|
|
277
|
+
configurable: false
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
Object.defineProperty(api, 'close', {
|
|
281
|
+
value: close,
|
|
282
|
+
writable: false,
|
|
283
|
+
enumerable: false,
|
|
284
|
+
configurable: false
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
// Auto-connect if APE_SERVER is set
|
|
288
|
+
if (serverUrl) {
|
|
289
|
+
connect()
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Export the same interface as browser
|
|
293
|
+
module.exports = api
|
|
294
|
+
module.exports.default = api
|
|
295
|
+
module.exports.on = on
|
|
296
|
+
module.exports.onConnectionChange = onConnectionChange
|
|
297
|
+
module.exports.connect = connect
|
|
298
|
+
module.exports.close = close
|
|
299
|
+
module.exports.ConnectionState = ConnectionState
|
package/server/index.js
CHANGED
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* api-ape server entry point
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* V3 Usage (100% identical on browser and server):
|
|
5
|
+
* import api from 'api-ape'
|
|
6
|
+
* api.hello('World') // Works same on browser AND server
|
|
7
|
+
* api.on('message', (data) => console.log(data))
|
|
8
|
+
*
|
|
9
|
+
* Server Setup:
|
|
10
|
+
* import api, { ape } from 'api-ape'
|
|
11
|
+
* ape(server, { where: 'api' }) // Start your server
|
|
12
|
+
*
|
|
13
|
+
* // Connect to another server (set APE_SERVER env or call api.connect)
|
|
14
|
+
* api.connect('ws://other-server:3000/api/ape')
|
|
15
|
+
* api.hello('World')
|
|
16
|
+
*
|
|
17
|
+
* Supports both CommonJS and ES Modules
|
|
4
18
|
*/
|
|
5
19
|
|
|
6
20
|
const ape = require('./lib/main')
|
|
7
21
|
const { broadcast, clients } = require('./lib/broadcast')
|
|
22
|
+
const api = require('./client')
|
|
8
23
|
|
|
9
|
-
// Attach broadcast utilities to the
|
|
24
|
+
// Attach broadcast utilities to the ape function
|
|
10
25
|
ape.broadcast = broadcast
|
|
11
26
|
ape.clients = clients
|
|
12
27
|
|
|
13
|
-
|
|
28
|
+
// Default export: api client (same interface as browser)
|
|
29
|
+
module.exports = api
|
|
30
|
+
|
|
31
|
+
// Named exports
|
|
32
|
+
module.exports.ape = ape
|
|
33
|
+
module.exports.api = api
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
14
37
|
|