api-ape 3.0.1 → 3.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.
@@ -5,15 +5,15 @@ Connect multiple api-ape server instances via a shared database for horizontal s
5
5
  ## Quick Start
6
6
 
7
7
  ```js
8
- import ape from 'api-ape/server';
9
- import { createClient } from 'redis';
8
+ const { ape } = require('api-ape')
9
+ const { createClient } = require('redis')
10
10
 
11
11
  // Connect to your database
12
- const redis = createClient();
13
- await redis.connect();
12
+ const redis = createClient()
13
+ await redis.connect()
14
14
 
15
15
  // Join the cluster — APE creates its own namespace
16
- ape.joinVia(redis);
16
+ ape.joinVia(redis)
17
17
  ```
18
18
 
19
19
  That's it. APE will:
package/server/client.js CHANGED
@@ -217,6 +217,12 @@ function onConnectionChange(handler) {
217
217
  */
218
218
  const handler = {
219
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
+
220
226
  // Reserved properties - same as browser
221
227
  if (prop === 'on') return on
222
228
  if (prop === 'onConnectionChange') return onConnectionChange
@@ -229,18 +235,13 @@ const handler = {
229
235
  const wrapperFn = function (a, b) {
230
236
  let path = joinKey + prop, body
231
237
  // Two args: first is path segment (string), second is body
232
- // One arg: it's the body (unless it's a string, then it's a path segment with no body)
233
- if (arguments.length === 2) {
238
+ // One arg: it's always the body (matches browser client behavior)
239
+ if (arguments.length === 2 && typeof a === 'string') {
234
240
  path += a
235
241
  body = b
236
- } else if (arguments.length === 1) {
237
- // If first arg is a string, treat as path segment, otherwise as body
238
- if (typeof a === 'string') {
239
- path += a
240
- body = undefined
241
- } else {
242
- body = a
243
- }
242
+ } else {
243
+ // Single arg or non-string first arg: treat first arg as body
244
+ body = a
244
245
  }
245
246
  return queueOrSend(path, body)
246
247
  }
@@ -305,4 +306,6 @@ module.exports.onConnectionChange = onConnectionChange
305
306
  module.exports.connect = connect
306
307
  module.exports.close = close
307
308
  module.exports.ConnectionState = ConnectionState
309
+ // Internal: expose queueOrSend for ape dual-purpose function
310
+ module.exports._queueOrSend = queueOrSend
308
311
 
package/server/index.js CHANGED
@@ -1,36 +1,90 @@
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))
4
+ * V3 Usage:
5
+ * const api = require('api-ape') // Get client proxy (default)
6
+ * const { ape } = require('api-ape') // Get server/API function
8
7
  *
9
- * Server Setup:
8
+ * // ESM
10
9
  * 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
10
  *
17
- * Supports both CommonJS and ES Modules
11
+ * Server Setup:
12
+ * ape(server, { where: 'api' }) // First arg is HTTP server → setup
13
+ *
14
+ * API Call:
15
+ * api.ape({ data: 'foo' }) // Calls /ape endpoint
16
+ * // or equivalently:
17
+ * ape({ data: 'foo' }) // Also calls /ape (detects it's not a server)
18
+ *
19
+ * The ape function intelligently detects:
20
+ * - HTTP server (has .listen/.on) → Server setup mode
21
+ * - Anything else → API call to /ape
18
22
  */
19
23
 
20
- const ape = require('./lib/main')
24
+ const serverApe = require('./lib/main')
21
25
  const { broadcast, clients } = require('./lib/broadcast')
22
26
  const api = require('./client')
27
+ const { _queueOrSend } = require('./client')
28
+
29
+ // Attach broadcast utilities to the serverApe function
30
+ serverApe.broadcast = broadcast
31
+ serverApe.clients = clients
32
+
33
+ /**
34
+ * Check if value looks like an HTTP server
35
+ */
36
+ function isHttpServer(val) {
37
+ return val && typeof val === 'object' && (
38
+ typeof val.listen === 'function' ||
39
+ typeof val.on === 'function' ||
40
+ typeof val.address === 'function'
41
+ )
42
+ }
43
+
44
+ /**
45
+ * Dual-purpose ape function:
46
+ * - Called with HTTP server → Setup server
47
+ * - Called with anything else → API call to /ape
48
+ */
49
+ function ape(firstArg, ...rest) {
50
+ if (isHttpServer(firstArg)) {
51
+ // Server setup mode
52
+ return serverApe(firstArg, ...rest)
53
+ }
54
+ // API call mode - directly call the internal queueOrSend
55
+ return _queueOrSend('/ape', firstArg)
56
+ }
23
57
 
24
- // Attach broadcast utilities to the ape function
58
+ // Copy properties from serverApe to ape
25
59
  ape.broadcast = broadcast
26
60
  ape.clients = clients
27
61
 
28
- // Default export: api client (same interface as browser)
62
+ // Store original serverApe for direct access if needed
63
+ ape._serverApe = serverApe
64
+
65
+ // Define ape on the proxy's target so it can be destructured
66
+ // The proxy handler checks Reflect.has first, so this will be found
67
+ Object.defineProperty(api, 'ape', {
68
+ value: ape,
69
+ writable: false,
70
+ enumerable: true,
71
+ configurable: false
72
+ })
73
+
74
+ // Default export: the proxy (so const api = require('api-ape') works)
29
75
  module.exports = api
30
76
 
31
- // Named exports
77
+ // Also export named exports for ESM compatibility
32
78
  module.exports.ape = ape
33
79
  module.exports.api = api
80
+ module.exports.broadcast = broadcast
81
+ module.exports.clients = clients
82
+ module.exports.default = api
83
+
84
+
85
+
86
+
87
+
34
88
 
35
89
 
36
90
 
@@ -201,9 +201,7 @@ function createLongPollingHandler(controllers, onConnect, fileTransfer) {
201
201
  req,
202
202
  broadcast: (t, d) => broadcast(t, d),
203
203
  broadcastOthers: (t, d) => broadcast(t, d, clientId),
204
- // Use clients Map for count and list
205
- online: () => clients.size,
206
- getClients: () => Array.from(clients.keys())
204
+ clients
207
205
  }
208
206
 
209
207
  // Execute controller
@@ -1,5 +1,5 @@
1
1
  const messageHash = require('../../utils/messageHash')
2
- const { broadcast, online, getClients } = require('../lib/broadcast')
2
+ const { broadcast, clients } = require('../lib/broadcast')
3
3
  const jss = require('../../utils/jss')
4
4
 
5
5
  /**
@@ -135,7 +135,7 @@ function getSessionId(req) {
135
135
  }
136
136
 
137
137
  module.exports = function receiveHandler(ape) {
138
- const { send, checkReply, events, controllers, sharedValues, hostId, embedValues, fileTransfer } = ape
138
+ const { send, checkReply, events, controllers, sharedValues, clientId, embedValues, fileTransfer } = ape
139
139
 
140
140
  // Extract sessionId from request cookies (set by outer framework session management)
141
141
  const sessionId = getSessionId(sharedValues.req)
@@ -147,10 +147,9 @@ module.exports = function receiveHandler(ape) {
147
147
  ...embedValues,
148
148
  // api-ape utilities available via `this`
149
149
  broadcast: (type, data) => broadcast(type, data),
150
- broadcastOthers: (type, data) => broadcast(type, data, hostId), // exclude self
151
- online,
152
- getClients,
153
- hostId,
150
+ broadcastOthers: (type, data) => broadcast(type, data, clientId), // exclude self
151
+ clients,
152
+ clientId,
154
153
  sessionId // Session ID from cookie (set by outer framework)
155
154
  }
156
155
 
@@ -181,7 +180,7 @@ module.exports = function receiveHandler(ape) {
181
180
  // Wait for all uploads
182
181
  try {
183
182
  await Promise.all(uploadTags.map(async ({ path, hash }) => {
184
- const uploadData = await fileTransfer.registerUpload(queryId, hash, hostId)
183
+ const uploadData = await fileTransfer.registerUpload(queryId, hash, clientId)
185
184
  setValueAtPath(processedData, path, uploadData)
186
185
  }))
187
186
  } catch (uploadErr) {
@@ -200,7 +199,7 @@ module.exports = function receiveHandler(ape) {
200
199
  if (fileTags.length > 0) {
201
200
  console.log(`📁 Registering ${fileTags.length} streaming file(s) for ${type}`)
202
201
  fileTags.forEach(({ hash }) => {
203
- fileTransfer.registerStreamingFile(hash, hostId)
202
+ fileTransfer.registerStreamingFile(hash, clientId)
204
203
  })
205
204
  }
206
205
  }
@@ -233,7 +232,7 @@ module.exports = function receiveHandler(ape) {
233
232
 
234
233
  } catch (err) {
235
234
  const errMessage = err.message || err
236
- events.onError(hostId, queryId, errMessage)
235
+ events.onError(clientId, queryId, errMessage)
237
236
  } // END catch
238
237
 
239
238
  } // END onReceive