api-ape 3.0.2 → 4.1.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 +59 -572
- package/client/README.md +73 -14
- package/client/auth/crypto/aead.js +214 -0
- package/client/auth/crypto/constants.js +32 -0
- package/client/auth/crypto/encoding.js +104 -0
- package/client/auth/crypto/files.md +27 -0
- package/client/auth/crypto/kdf.js +217 -0
- package/client/auth/crypto-utils.js +118 -0
- package/client/auth/files.md +52 -0
- package/client/auth/key-recovery.js +288 -0
- package/client/auth/recovery/constants.js +37 -0
- package/client/auth/recovery/files.md +23 -0
- package/client/auth/recovery/key-derivation.js +61 -0
- package/client/auth/recovery/sss-browser.js +189 -0
- package/client/auth/share-storage.js +205 -0
- package/client/auth/storage/constants.js +18 -0
- package/client/auth/storage/db.js +132 -0
- package/client/auth/storage/files.md +27 -0
- package/client/auth/storage/keys.js +173 -0
- package/client/auth/storage/shares.js +200 -0
- package/client/browser.js +190 -23
- package/client/connectSocket.js +418 -988
- package/client/connection/README.md +23 -0
- package/client/connection/fileDownload.js +256 -0
- package/client/connection/fileHandling.js +450 -0
- package/client/connection/fileUtils.js +346 -0
- package/client/connection/files.md +71 -0
- package/client/connection/messageHandler.js +105 -0
- package/client/connection/network.js +350 -0
- package/client/connection/proxy.js +233 -0
- package/client/connection/sender.js +333 -0
- package/client/connection/state.js +321 -0
- package/client/connection/subscriptions.js +151 -0
- package/client/files.md +53 -0
- package/client/index.js +298 -142
- package/client/transports/README.md +50 -0
- package/client/transports/files.md +41 -0
- package/client/transports/streamParser.js +195 -0
- package/client/transports/streaming.js +555 -203
- package/dist/ape.js +6 -1
- package/dist/ape.js.map +4 -4
- package/index.d.ts +38 -16
- package/package.json +31 -6
- package/server/README.md +272 -67
- package/server/adapters/README.md +23 -14
- package/server/adapters/files.md +68 -0
- package/server/adapters/firebase.js +543 -160
- package/server/adapters/index.js +362 -112
- package/server/adapters/mongo.js +530 -140
- package/server/adapters/postgres.js +534 -155
- package/server/adapters/redis.js +508 -143
- package/server/adapters/supabase.js +555 -186
- package/server/client/README.md +43 -0
- package/server/client/connection.js +586 -0
- package/server/client/files.md +40 -0
- package/server/client/index.js +342 -0
- package/server/files.md +54 -0
- package/server/index.js +322 -71
- package/server/lib/README.md +26 -0
- package/server/lib/broadcast/clients.js +219 -0
- package/server/lib/broadcast/files.md +58 -0
- package/server/lib/broadcast/index.js +57 -0
- package/server/lib/broadcast/publishProxy.js +110 -0
- package/server/lib/broadcast/pubsub.js +137 -0
- package/server/lib/broadcast/sendProxy.js +103 -0
- package/server/lib/bun.js +315 -99
- package/server/lib/fileTransfer/README.md +63 -0
- package/server/lib/fileTransfer/files.md +30 -0
- package/server/lib/fileTransfer/streaming.js +435 -0
- package/server/lib/fileTransfer.js +710 -326
- package/server/lib/files.md +111 -0
- package/server/lib/httpUtils.js +283 -0
- package/server/lib/loader.js +208 -7
- package/server/lib/longPolling/README.md +63 -0
- package/server/lib/longPolling/files.md +44 -0
- package/server/lib/longPolling/getHandler.js +365 -0
- package/server/lib/longPolling/postHandler.js +327 -0
- package/server/lib/longPolling.js +174 -219
- package/server/lib/main.js +369 -532
- package/server/lib/runtimes/README.md +42 -0
- package/server/lib/runtimes/bun.js +586 -0
- package/server/lib/runtimes/files.md +56 -0
- package/server/lib/runtimes/node.js +511 -0
- package/server/lib/wiring.js +539 -98
- package/server/lib/ws/README.md +35 -0
- package/server/lib/ws/adapters/README.md +54 -0
- package/server/lib/ws/adapters/bun.js +538 -170
- package/server/lib/ws/adapters/deno.js +623 -149
- package/server/lib/ws/adapters/files.md +42 -0
- package/server/lib/ws/files.md +74 -0
- package/server/lib/ws/frames.js +532 -154
- package/server/lib/ws/index.js +207 -10
- package/server/lib/ws/server.js +385 -92
- package/server/lib/ws/socket.js +549 -181
- package/server/lib/wsProvider.js +363 -89
- package/server/plugins/binary.js +282 -0
- package/server/security/README.md +92 -0
- package/server/security/auth/README.md +319 -0
- package/server/security/auth/adapters/files.md +95 -0
- package/server/security/auth/adapters/ldap/constants.js +37 -0
- package/server/security/auth/adapters/ldap/files.md +19 -0
- package/server/security/auth/adapters/ldap/helpers.js +111 -0
- package/server/security/auth/adapters/ldap.js +353 -0
- package/server/security/auth/adapters/oauth2/constants.js +41 -0
- package/server/security/auth/adapters/oauth2/files.md +19 -0
- package/server/security/auth/adapters/oauth2/helpers.js +123 -0
- package/server/security/auth/adapters/oauth2.js +273 -0
- package/server/security/auth/adapters/opaque-handlers.js +314 -0
- package/server/security/auth/adapters/opaque.js +205 -0
- package/server/security/auth/adapters/saml/constants.js +52 -0
- package/server/security/auth/adapters/saml/files.md +19 -0
- package/server/security/auth/adapters/saml/helpers.js +74 -0
- package/server/security/auth/adapters/saml.js +173 -0
- package/server/security/auth/adapters/totp.js +703 -0
- package/server/security/auth/adapters/webauthn.js +625 -0
- package/server/security/auth/files.md +61 -0
- package/server/security/auth/framework/constants.js +27 -0
- package/server/security/auth/framework/files.md +23 -0
- package/server/security/auth/framework/handlers.js +272 -0
- package/server/security/auth/framework/socket-auth.js +177 -0
- package/server/security/auth/handlers/auth-messages.js +143 -0
- package/server/security/auth/handlers/files.md +28 -0
- package/server/security/auth/index.js +290 -0
- package/server/security/auth/mfa/crypto/aead.js +148 -0
- package/server/security/auth/mfa/crypto/constants.js +35 -0
- package/server/security/auth/mfa/crypto/files.md +27 -0
- package/server/security/auth/mfa/crypto/kdf.js +120 -0
- package/server/security/auth/mfa/crypto/utils.js +68 -0
- package/server/security/auth/mfa/crypto-utils.js +80 -0
- package/server/security/auth/mfa/files.md +77 -0
- package/server/security/auth/mfa/ledger/constants.js +75 -0
- package/server/security/auth/mfa/ledger/errors.js +73 -0
- package/server/security/auth/mfa/ledger/files.md +23 -0
- package/server/security/auth/mfa/ledger/share-record.js +32 -0
- package/server/security/auth/mfa/ledger.js +255 -0
- package/server/security/auth/mfa/recovery/constants.js +67 -0
- package/server/security/auth/mfa/recovery/files.md +19 -0
- package/server/security/auth/mfa/recovery/handlers.js +216 -0
- package/server/security/auth/mfa/recovery.js +191 -0
- package/server/security/auth/mfa/sss/constants.js +21 -0
- package/server/security/auth/mfa/sss/files.md +23 -0
- package/server/security/auth/mfa/sss/gf256.js +103 -0
- package/server/security/auth/mfa/sss/serialization.js +82 -0
- package/server/security/auth/mfa/sss.js +161 -0
- package/server/security/auth/mfa/two-of-three/constants.js +58 -0
- package/server/security/auth/mfa/two-of-three/files.md +23 -0
- package/server/security/auth/mfa/two-of-three/handlers.js +241 -0
- package/server/security/auth/mfa/two-of-three/helpers.js +71 -0
- package/server/security/auth/mfa/two-of-three.js +136 -0
- package/server/security/auth/nonce-manager.js +89 -0
- package/server/security/auth/state-machine-mfa.js +269 -0
- package/server/security/auth/state-machine.js +257 -0
- package/server/security/extractRootDomain.js +144 -16
- package/server/security/files.md +51 -0
- package/server/security/origin.js +197 -15
- package/server/security/reply.js +274 -16
- package/server/socket/README.md +119 -0
- package/server/socket/authMiddleware.js +299 -0
- package/server/socket/files.md +86 -0
- package/server/socket/open.js +154 -8
- package/server/socket/pluginHooks.js +334 -0
- package/server/socket/receive.js +184 -224
- package/server/socket/receiveContext.js +117 -0
- package/server/socket/send.js +416 -78
- package/server/socket/tagUtils.js +402 -0
- package/server/utils/README.md +19 -0
- package/server/utils/deepRequire.js +255 -30
- package/server/utils/files.md +57 -0
- package/server/utils/genId.js +182 -20
- package/server/utils/parseUserAgent.js +313 -251
- package/server/utils/userAgent/README.md +65 -0
- package/server/utils/userAgent/files.md +46 -0
- package/server/utils/userAgent/patterns.js +545 -0
- package/utils/README.md +21 -0
- package/utils/files.md +66 -0
- package/utils/jss/README.md +21 -0
- package/utils/jss/decode.js +471 -0
- package/utils/jss/encode.js +312 -0
- package/utils/jss/files.md +68 -0
- package/utils/jss/plugins.js +210 -0
- package/utils/jss.js +219 -273
- package/utils/messageHash.js +238 -35
- package/dist/api-ape.min.js +0 -2
- package/dist/api-ape.min.js.map +0 -7
- package/server/client.js +0 -311
- package/server/lib/broadcast.js +0 -146
package/server/client.js
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
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
|
-
// First check if property exists on target (set via defineProperty)
|
|
221
|
-
// This allows named exports like 'ape' to be accessed directly
|
|
222
|
-
if (Reflect.has(target, prop)) {
|
|
223
|
-
return Reflect.get(target, prop)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Reserved properties - same as browser
|
|
227
|
-
if (prop === 'on') return on
|
|
228
|
-
if (prop === 'onConnectionChange') return onConnectionChange
|
|
229
|
-
if (prop === 'transport') return ready ? 'websocket' : null
|
|
230
|
-
if (prop === 'connect') return connect
|
|
231
|
-
if (prop === 'close') return close
|
|
232
|
-
if (prop === 'then' || prop === 'catch') return undefined // Not a Promise
|
|
233
|
-
|
|
234
|
-
// Return a function that either calls directly or buffers
|
|
235
|
-
const wrapperFn = function (a, b) {
|
|
236
|
-
let path = joinKey + prop, body
|
|
237
|
-
// Two args: first is path segment (string), second is body
|
|
238
|
-
// One arg: it's always the body (matches browser client behavior)
|
|
239
|
-
if (arguments.length === 2 && typeof a === 'string') {
|
|
240
|
-
path += a
|
|
241
|
-
body = b
|
|
242
|
-
} else {
|
|
243
|
-
// Single arg or non-string first arg: treat first arg as body
|
|
244
|
-
body = a
|
|
245
|
-
}
|
|
246
|
-
return queueOrSend(path, body)
|
|
247
|
-
}
|
|
248
|
-
return new Proxy(wrapperFn, handler)
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function close() {
|
|
253
|
-
reconnectEnabled = false
|
|
254
|
-
if (reconnectTimer) {
|
|
255
|
-
clearTimeout(reconnectTimer)
|
|
256
|
-
reconnectTimer = null
|
|
257
|
-
}
|
|
258
|
-
if (ws) {
|
|
259
|
-
notifyConnectionChange(ConnectionState.Closing)
|
|
260
|
-
ws.close()
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Create the proxy (same interface as browser senderProxy)
|
|
265
|
-
const api = new Proxy({}, handler)
|
|
266
|
-
|
|
267
|
-
// Define properties on the proxy (same as browser)
|
|
268
|
-
Object.defineProperty(api, 'on', {
|
|
269
|
-
value: on,
|
|
270
|
-
writable: false,
|
|
271
|
-
enumerable: false,
|
|
272
|
-
configurable: false
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
Object.defineProperty(api, 'onConnectionChange', {
|
|
276
|
-
value: onConnectionChange,
|
|
277
|
-
writable: false,
|
|
278
|
-
enumerable: false,
|
|
279
|
-
configurable: false
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
Object.defineProperty(api, 'connect', {
|
|
283
|
-
value: connect,
|
|
284
|
-
writable: false,
|
|
285
|
-
enumerable: false,
|
|
286
|
-
configurable: false
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
Object.defineProperty(api, 'close', {
|
|
290
|
-
value: close,
|
|
291
|
-
writable: false,
|
|
292
|
-
enumerable: false,
|
|
293
|
-
configurable: false
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
// NOTE: We do NOT auto-connect on module load, even if APE_SERVER is set.
|
|
297
|
-
// Connection only happens when:
|
|
298
|
-
// 1. api.connect(url) is called explicitly
|
|
299
|
-
// 2. A method is called and APE_SERVER env is set (lazy connection)
|
|
300
|
-
|
|
301
|
-
// Export the same interface as browser
|
|
302
|
-
module.exports = api
|
|
303
|
-
module.exports.default = api
|
|
304
|
-
module.exports.on = on
|
|
305
|
-
module.exports.onConnectionChange = onConnectionChange
|
|
306
|
-
module.exports.connect = connect
|
|
307
|
-
module.exports.close = close
|
|
308
|
-
module.exports.ConnectionState = ConnectionState
|
|
309
|
-
// Internal: expose queueOrSend for ape dual-purpose function
|
|
310
|
-
module.exports._queueOrSend = queueOrSend
|
|
311
|
-
|
package/server/lib/broadcast.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client tracking and broadcast utilities for api-ape
|
|
3
|
-
* Provides a Map of connected clients with sendTo functionality
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// Internal Map of connected clients: clientId -> ClientWrapper
|
|
7
|
-
const _clients = new Map()
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Create a ClientWrapper that exposes client info and sendTo function
|
|
11
|
-
* @param {object} clientInfo - Raw client info from wiring/longPolling
|
|
12
|
-
*/
|
|
13
|
-
function createClientWrapper(clientInfo) {
|
|
14
|
-
return {
|
|
15
|
-
get clientId() { return clientInfo.clientId },
|
|
16
|
-
get sessionId() { return clientInfo.sessionId || null },
|
|
17
|
-
get embed() { return clientInfo.embed || {} },
|
|
18
|
-
get agent() { return clientInfo.agent || {} },
|
|
19
|
-
/**
|
|
20
|
-
* Send a message to this specific client
|
|
21
|
-
* @param {string} type - Message type
|
|
22
|
-
* @param {any} data - Data to send
|
|
23
|
-
*/
|
|
24
|
-
sendTo(type, data) {
|
|
25
|
-
if (clientInfo.send) {
|
|
26
|
-
try {
|
|
27
|
-
clientInfo.send(false, type, data, false)
|
|
28
|
-
} catch (e) {
|
|
29
|
-
console.error(`📢 sendTo failed for ${clientInfo.clientId}:`, e.message)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Read-only proxy for the clients Map
|
|
38
|
-
* Allows: get, has, keys, values, entries, forEach, size, iteration
|
|
39
|
-
* Prevents: set, delete, clear (throws error if attempted)
|
|
40
|
-
*/
|
|
41
|
-
const clients = new Proxy(_clients, {
|
|
42
|
-
get(target, prop) {
|
|
43
|
-
// Prevent mutation methods
|
|
44
|
-
if (prop === 'set' || prop === 'delete' || prop === 'clear') {
|
|
45
|
-
return () => {
|
|
46
|
-
throw new Error(`ape.clients.${prop}() is not allowed. Clients are managed internally by api-ape.`)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Allow size property
|
|
51
|
-
if (prop === 'size') {
|
|
52
|
-
return target.size
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Bind methods to target
|
|
56
|
-
const value = target[prop]
|
|
57
|
-
if (typeof value === 'function') {
|
|
58
|
-
return value.bind(target)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return value
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Add a client to the connected map (internal use only)
|
|
67
|
-
* @param {object} clientInfo - { clientId, sessionId, agent, embed, send }
|
|
68
|
-
*/
|
|
69
|
-
function addClient(clientInfo) {
|
|
70
|
-
const wrapper = createClientWrapper(clientInfo)
|
|
71
|
-
_clients.set(clientInfo.clientId, wrapper)
|
|
72
|
-
|
|
73
|
-
// Store reference to raw info so we can update embed later if needed
|
|
74
|
-
wrapper._raw = clientInfo
|
|
75
|
-
|
|
76
|
-
console.log(`🟢 Client added: ${clientInfo.clientId} (total: ${_clients.size})`)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Remove a client from the connected map (internal use only)
|
|
81
|
-
* @param {string|object} clientIdOrInfo - clientId string or { clientId } object
|
|
82
|
-
*/
|
|
83
|
-
function removeClient(clientIdOrInfo) {
|
|
84
|
-
const clientId = typeof clientIdOrInfo === 'string'
|
|
85
|
-
? clientIdOrInfo
|
|
86
|
-
: clientIdOrInfo.clientId
|
|
87
|
-
|
|
88
|
-
if (_clients.has(clientId)) {
|
|
89
|
-
_clients.delete(clientId)
|
|
90
|
-
console.log(`🔴 Client removed: ${clientId} (total: ${_clients.size})`)
|
|
91
|
-
} else {
|
|
92
|
-
console.log(`⚠️ Client not found for removal: ${clientId} (total: ${_clients.size})`)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Update a client's embed values after onConnect resolves (internal use only)
|
|
98
|
-
* @param {string} clientId
|
|
99
|
-
* @param {object} embed
|
|
100
|
-
*/
|
|
101
|
-
function updateClientEmbed(clientId, embed) {
|
|
102
|
-
const wrapper = _clients.get(clientId)
|
|
103
|
-
if (wrapper && wrapper._raw) {
|
|
104
|
-
wrapper._raw.embed = embed
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Update a client's send function after it's ready (internal use only)
|
|
110
|
-
* @param {string} clientId
|
|
111
|
-
* @param {function} send
|
|
112
|
-
*/
|
|
113
|
-
function updateClientSend(clientId, send) {
|
|
114
|
-
const wrapper = _clients.get(clientId)
|
|
115
|
-
if (wrapper && wrapper._raw) {
|
|
116
|
-
wrapper._raw.send = send
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Broadcast to all connected clients
|
|
122
|
-
* @param {string} type - Message type
|
|
123
|
-
* @param {any} data - Data to send
|
|
124
|
-
* @param {string} [excludeClientId] - Optional clientId to exclude (e.g., sender)
|
|
125
|
-
*/
|
|
126
|
-
function broadcast(type, data, excludeClientId) {
|
|
127
|
-
console.log(`📢 Broadcasting "${type}" to ${_clients.size} clients`, excludeClientId ? `(excluding ${excludeClientId})` : '')
|
|
128
|
-
_clients.forEach((wrapper, clientId) => {
|
|
129
|
-
if (excludeClientId && clientId === excludeClientId) {
|
|
130
|
-
return // Skip excluded client
|
|
131
|
-
}
|
|
132
|
-
wrapper.sendTo(type, data)
|
|
133
|
-
})
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
module.exports = {
|
|
137
|
-
// Public: read-only clients Map
|
|
138
|
-
clients,
|
|
139
|
-
// Public: broadcast function
|
|
140
|
-
broadcast,
|
|
141
|
-
// Internal: client management (used by wiring.js and longPolling.js)
|
|
142
|
-
addClient,
|
|
143
|
-
removeClient,
|
|
144
|
-
updateClientEmbed,
|
|
145
|
-
updateClientSend
|
|
146
|
-
}
|