api-ape 3.0.1 → 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.
Files changed (186) hide show
  1. package/README.md +58 -570
  2. package/client/README.md +73 -14
  3. package/client/auth/crypto/aead.js +214 -0
  4. package/client/auth/crypto/constants.js +32 -0
  5. package/client/auth/crypto/encoding.js +104 -0
  6. package/client/auth/crypto/files.md +27 -0
  7. package/client/auth/crypto/kdf.js +217 -0
  8. package/client/auth/crypto-utils.js +118 -0
  9. package/client/auth/files.md +52 -0
  10. package/client/auth/key-recovery.js +288 -0
  11. package/client/auth/recovery/constants.js +37 -0
  12. package/client/auth/recovery/files.md +23 -0
  13. package/client/auth/recovery/key-derivation.js +61 -0
  14. package/client/auth/recovery/sss-browser.js +189 -0
  15. package/client/auth/share-storage.js +205 -0
  16. package/client/auth/storage/constants.js +18 -0
  17. package/client/auth/storage/db.js +132 -0
  18. package/client/auth/storage/files.md +27 -0
  19. package/client/auth/storage/keys.js +173 -0
  20. package/client/auth/storage/shares.js +200 -0
  21. package/client/browser.js +190 -23
  22. package/client/connectSocket.js +418 -988
  23. package/client/connection/README.md +23 -0
  24. package/client/connection/fileDownload.js +256 -0
  25. package/client/connection/fileHandling.js +450 -0
  26. package/client/connection/fileUtils.js +346 -0
  27. package/client/connection/files.md +71 -0
  28. package/client/connection/messageHandler.js +105 -0
  29. package/client/connection/network.js +350 -0
  30. package/client/connection/proxy.js +233 -0
  31. package/client/connection/sender.js +333 -0
  32. package/client/connection/state.js +321 -0
  33. package/client/connection/subscriptions.js +151 -0
  34. package/client/files.md +53 -0
  35. package/client/index.js +298 -142
  36. package/client/transports/README.md +50 -0
  37. package/client/transports/files.md +41 -0
  38. package/client/transports/streamParser.js +195 -0
  39. package/client/transports/streaming.js +555 -202
  40. package/dist/ape.js +6 -1
  41. package/dist/ape.js.map +4 -4
  42. package/index.d.ts +38 -16
  43. package/package.json +32 -7
  44. package/server/README.md +287 -53
  45. package/server/adapters/README.md +28 -19
  46. package/server/adapters/files.md +68 -0
  47. package/server/adapters/firebase.js +543 -160
  48. package/server/adapters/index.js +362 -112
  49. package/server/adapters/mongo.js +530 -140
  50. package/server/adapters/postgres.js +534 -155
  51. package/server/adapters/redis.js +508 -143
  52. package/server/adapters/supabase.js +555 -186
  53. package/server/client/README.md +43 -0
  54. package/server/client/connection.js +586 -0
  55. package/server/client/files.md +40 -0
  56. package/server/client/index.js +342 -0
  57. package/server/files.md +54 -0
  58. package/server/index.js +332 -27
  59. package/server/lib/README.md +26 -0
  60. package/server/lib/broadcast/clients.js +219 -0
  61. package/server/lib/broadcast/files.md +58 -0
  62. package/server/lib/broadcast/index.js +57 -0
  63. package/server/lib/broadcast/publishProxy.js +110 -0
  64. package/server/lib/broadcast/pubsub.js +137 -0
  65. package/server/lib/broadcast/sendProxy.js +103 -0
  66. package/server/lib/bun.js +315 -99
  67. package/server/lib/fileTransfer/README.md +63 -0
  68. package/server/lib/fileTransfer/files.md +30 -0
  69. package/server/lib/fileTransfer/streaming.js +435 -0
  70. package/server/lib/fileTransfer.js +710 -326
  71. package/server/lib/files.md +111 -0
  72. package/server/lib/httpUtils.js +283 -0
  73. package/server/lib/loader.js +208 -7
  74. package/server/lib/longPolling/README.md +63 -0
  75. package/server/lib/longPolling/files.md +44 -0
  76. package/server/lib/longPolling/getHandler.js +365 -0
  77. package/server/lib/longPolling/postHandler.js +327 -0
  78. package/server/lib/longPolling.js +174 -221
  79. package/server/lib/main.js +369 -532
  80. package/server/lib/runtimes/README.md +42 -0
  81. package/server/lib/runtimes/bun.js +586 -0
  82. package/server/lib/runtimes/files.md +56 -0
  83. package/server/lib/runtimes/node.js +511 -0
  84. package/server/lib/wiring.js +539 -98
  85. package/server/lib/ws/README.md +35 -0
  86. package/server/lib/ws/adapters/README.md +54 -0
  87. package/server/lib/ws/adapters/bun.js +538 -170
  88. package/server/lib/ws/adapters/deno.js +623 -149
  89. package/server/lib/ws/adapters/files.md +42 -0
  90. package/server/lib/ws/files.md +74 -0
  91. package/server/lib/ws/frames.js +532 -154
  92. package/server/lib/ws/index.js +207 -10
  93. package/server/lib/ws/server.js +385 -92
  94. package/server/lib/ws/socket.js +549 -181
  95. package/server/lib/wsProvider.js +363 -89
  96. package/server/plugins/binary.js +282 -0
  97. package/server/security/README.md +92 -0
  98. package/server/security/auth/README.md +319 -0
  99. package/server/security/auth/adapters/files.md +95 -0
  100. package/server/security/auth/adapters/ldap/constants.js +37 -0
  101. package/server/security/auth/adapters/ldap/files.md +19 -0
  102. package/server/security/auth/adapters/ldap/helpers.js +111 -0
  103. package/server/security/auth/adapters/ldap.js +353 -0
  104. package/server/security/auth/adapters/oauth2/constants.js +41 -0
  105. package/server/security/auth/adapters/oauth2/files.md +19 -0
  106. package/server/security/auth/adapters/oauth2/helpers.js +123 -0
  107. package/server/security/auth/adapters/oauth2.js +273 -0
  108. package/server/security/auth/adapters/opaque-handlers.js +314 -0
  109. package/server/security/auth/adapters/opaque.js +205 -0
  110. package/server/security/auth/adapters/saml/constants.js +52 -0
  111. package/server/security/auth/adapters/saml/files.md +19 -0
  112. package/server/security/auth/adapters/saml/helpers.js +74 -0
  113. package/server/security/auth/adapters/saml.js +173 -0
  114. package/server/security/auth/adapters/totp.js +703 -0
  115. package/server/security/auth/adapters/webauthn.js +625 -0
  116. package/server/security/auth/files.md +61 -0
  117. package/server/security/auth/framework/constants.js +27 -0
  118. package/server/security/auth/framework/files.md +23 -0
  119. package/server/security/auth/framework/handlers.js +272 -0
  120. package/server/security/auth/framework/socket-auth.js +177 -0
  121. package/server/security/auth/handlers/auth-messages.js +143 -0
  122. package/server/security/auth/handlers/files.md +28 -0
  123. package/server/security/auth/index.js +290 -0
  124. package/server/security/auth/mfa/crypto/aead.js +148 -0
  125. package/server/security/auth/mfa/crypto/constants.js +35 -0
  126. package/server/security/auth/mfa/crypto/files.md +27 -0
  127. package/server/security/auth/mfa/crypto/kdf.js +120 -0
  128. package/server/security/auth/mfa/crypto/utils.js +68 -0
  129. package/server/security/auth/mfa/crypto-utils.js +80 -0
  130. package/server/security/auth/mfa/files.md +77 -0
  131. package/server/security/auth/mfa/ledger/constants.js +75 -0
  132. package/server/security/auth/mfa/ledger/errors.js +73 -0
  133. package/server/security/auth/mfa/ledger/files.md +23 -0
  134. package/server/security/auth/mfa/ledger/share-record.js +32 -0
  135. package/server/security/auth/mfa/ledger.js +255 -0
  136. package/server/security/auth/mfa/recovery/constants.js +67 -0
  137. package/server/security/auth/mfa/recovery/files.md +19 -0
  138. package/server/security/auth/mfa/recovery/handlers.js +216 -0
  139. package/server/security/auth/mfa/recovery.js +191 -0
  140. package/server/security/auth/mfa/sss/constants.js +21 -0
  141. package/server/security/auth/mfa/sss/files.md +23 -0
  142. package/server/security/auth/mfa/sss/gf256.js +103 -0
  143. package/server/security/auth/mfa/sss/serialization.js +82 -0
  144. package/server/security/auth/mfa/sss.js +161 -0
  145. package/server/security/auth/mfa/two-of-three/constants.js +58 -0
  146. package/server/security/auth/mfa/two-of-three/files.md +23 -0
  147. package/server/security/auth/mfa/two-of-three/handlers.js +241 -0
  148. package/server/security/auth/mfa/two-of-three/helpers.js +71 -0
  149. package/server/security/auth/mfa/two-of-three.js +136 -0
  150. package/server/security/auth/nonce-manager.js +89 -0
  151. package/server/security/auth/state-machine-mfa.js +269 -0
  152. package/server/security/auth/state-machine.js +257 -0
  153. package/server/security/extractRootDomain.js +144 -16
  154. package/server/security/files.md +51 -0
  155. package/server/security/origin.js +197 -15
  156. package/server/security/reply.js +274 -16
  157. package/server/socket/README.md +119 -0
  158. package/server/socket/authMiddleware.js +299 -0
  159. package/server/socket/files.md +86 -0
  160. package/server/socket/open.js +154 -8
  161. package/server/socket/pluginHooks.js +334 -0
  162. package/server/socket/receive.js +184 -225
  163. package/server/socket/receiveContext.js +117 -0
  164. package/server/socket/send.js +416 -78
  165. package/server/socket/tagUtils.js +402 -0
  166. package/server/utils/README.md +19 -0
  167. package/server/utils/deepRequire.js +255 -30
  168. package/server/utils/files.md +57 -0
  169. package/server/utils/genId.js +182 -20
  170. package/server/utils/parseUserAgent.js +313 -251
  171. package/server/utils/userAgent/README.md +65 -0
  172. package/server/utils/userAgent/files.md +46 -0
  173. package/server/utils/userAgent/patterns.js +545 -0
  174. package/utils/README.md +21 -0
  175. package/utils/files.md +66 -0
  176. package/utils/jss/README.md +21 -0
  177. package/utils/jss/decode.js +471 -0
  178. package/utils/jss/encode.js +312 -0
  179. package/utils/jss/files.md +68 -0
  180. package/utils/jss/plugins.js +210 -0
  181. package/utils/jss.js +219 -273
  182. package/utils/messageHash.js +238 -35
  183. package/dist/api-ape.min.js +0 -2
  184. package/dist/api-ape.min.js.map +0 -7
  185. package/server/client.js +0 -308
  186. package/server/lib/broadcast.js +0 -146
package/server/lib/bun.js CHANGED
@@ -1,122 +1,338 @@
1
1
  /**
2
- * Bun-specific api-ape integration
3
- * Returns handlers for use with Bun.serve()
4
- *
5
- * Usage:
6
- * ```ts
2
+ * @fileoverview Bun-Specific api-ape Integration
3
+ *
4
+ * This module provides Bun-native integration for api-ape servers.
5
+ * It returns handlers that can be directly used with `Bun.serve()`,
6
+ * taking advantage of Bun's high-performance WebSocket implementation.
7
+ *
8
+ * Bun has a unique server API compared to Node.js:
9
+ * - WebSocket upgrades are handled via `server.upgrade()` in the fetch handler
10
+ * - WebSocket events are handled via a separate `websocket` object
11
+ * - The `fetch` function handles all HTTP requests
12
+ *
13
+ * This module provides a seamless integration that:
14
+ * - Handles WebSocket upgrades for the api-ape endpoint
15
+ * - Serves the client JavaScript bundle
16
+ * - Wraps Bun's native WebSocket in a ws-compatible interface
17
+ *
18
+ * @module server/lib/bun
19
+ * @see {@link module:server/lib/runtimes/bun} - Runtime-specific initialization
20
+ * @see {@link module:server/lib/ws/adapters/bun} - Bun WebSocket adapter
21
+ *
22
+ * @example
23
+ * // Basic usage with Bun.serve()
7
24
  * import { apeBun } from 'api-ape/bun'
8
- *
9
- * const ape = apeBun({ where: 'api', onConnect: ... })
10
- *
25
+ *
26
+ * const ape = apeBun({
27
+ * where: 'api',
28
+ * onConnect: (socket, req, send) => {
29
+ * console.log('Client connected')
30
+ * return {
31
+ * onDisconnect: () => console.log('Client disconnected'),
32
+ * embed: { userId: 'user-123' }
33
+ * }
34
+ * }
35
+ * })
36
+ *
37
+ * Bun.serve({
38
+ * port: 3000,
39
+ * fetch: ape.fetch,
40
+ * websocket: ape.websocket
41
+ * })
42
+ *
43
+ * @example
44
+ * // Combined with custom routes
11
45
  * Bun.serve({
12
- * port: 3000,
13
- * fetch: ape.fetch,
14
- * websocket: ape.websocket
46
+ * port: 3000,
47
+ * fetch(req, server) {
48
+ * // Try api-ape routes first
49
+ * const apeResponse = ape.fetch(req, server)
50
+ * if (apeResponse !== null) return apeResponse
51
+ *
52
+ * // Custom routes
53
+ * const url = new URL(req.url)
54
+ * if (url.pathname === '/health') {
55
+ * return new Response('OK')
56
+ * }
57
+ *
58
+ * return new Response('Not Found', { status: 404 })
59
+ * },
60
+ * websocket: ape.websocket
15
61
  * })
16
- * ```
17
62
  */
18
63
 
19
- const loader = require('./loader')
20
- const wiring = require('./wiring')
21
- const path = require('path')
22
- const fs = require('fs')
23
- const { getFileTransferManager } = require('./fileTransfer')
24
- const { BunWebSocket, BunWebSocketServer } = require('./ws/adapters/bun')
64
+ const loader = require("./loader");
65
+ const wiring = require("./wiring");
66
+ const path = require("path");
67
+ const fs = require("fs");
68
+ const { getFileTransferManager } = require("./fileTransfer");
69
+ const { BunWebSocket, BunWebSocketServer } = require("./ws/adapters/bun");
70
+
71
+ /**
72
+ * @typedef {Object} ApeBunOptions
73
+ * Configuration options for the Bun api-ape integration.
74
+ *
75
+ * @property {string} where - Directory containing controller files (relative to cwd).
76
+ * Controllers in this directory become API endpoints.
77
+ * Example: 'api' means controllers in './api/' folder.
78
+ * @property {Function} [onConnect] - Callback when a client connects.
79
+ * Receives (socket, req, send) and can return { onDisconnect, embed }.
80
+ * @property {Object} [fileTransferOptions] - Options for the file transfer manager.
81
+ */
82
+
83
+ /**
84
+ * @typedef {Object} ApeBunResult
85
+ * The result object containing handlers for Bun.serve().
86
+ *
87
+ * @property {Function} fetch - HTTP request handler for Bun.serve().
88
+ * Returns Response for api-ape routes, null for non-matching routes.
89
+ * @property {Object} websocket - WebSocket event handlers for Bun.serve().
90
+ * @property {BunWebSocketServer} wss - The WebSocket server instance.
91
+ */
25
92
 
26
93
  /**
27
- * Create api-ape handlers for Bun.serve()
28
- * @param {{ where: string, onConnect?: Function, fileTransferOptions?: Object }} options
94
+ * @typedef {Object} BunWebSocketHandlers
95
+ * WebSocket event handlers for Bun.serve().
96
+ *
97
+ * @property {Function} open - Called when a WebSocket connection opens
98
+ * @property {Function} message - Called when a message is received
99
+ * @property {Function} close - Called when a connection closes
100
+ * @property {Function} error - Called when an error occurs
101
+ */
102
+
103
+ /**
104
+ * Creates api-ape handlers for use with Bun.serve().
105
+ *
106
+ * This function sets up everything needed to run api-ape on Bun:
107
+ * - Loads controllers from the specified directory
108
+ * - Creates a WebSocket server with Bun-native adapter
109
+ * - Returns fetch and websocket handlers for Bun.serve()
110
+ *
111
+ * The returned `fetch` handler:
112
+ * - Handles WebSocket upgrades at `/{where}/ape`
113
+ * - Serves the client bundle at `/{where}/ape.js`
114
+ * - Returns `null` for non-matching routes (allows fallthrough)
115
+ *
116
+ * The returned `websocket` handler:
117
+ * - Wraps Bun's native WebSocket in ws-compatible interface
118
+ * - Routes messages through api-ape's wiring layer
119
+ * - Handles connection lifecycle (open, message, close, error)
120
+ *
121
+ * @function apeBun
122
+ * @param {ApeBunOptions} options - Configuration options
123
+ * @param {string} options.where - Controller directory name
124
+ * @param {Function} [options.onConnect] - Connection callback
125
+ * @param {Object} [options.fileTransferOptions] - File transfer options
126
+ * @returns {ApeBunResult} Object with fetch, websocket, and wss properties
127
+ *
128
+ * @example
129
+ * // Minimal setup
130
+ * const ape = apeBun({ where: 'api' })
131
+ *
132
+ * Bun.serve({
133
+ * port: 3000,
134
+ * fetch: ape.fetch,
135
+ * websocket: ape.websocket
136
+ * })
137
+ *
138
+ * @example
139
+ * // With connection handler
140
+ * const ape = apeBun({
141
+ * where: 'api',
142
+ * onConnect: async (socket, req, send) => {
143
+ * // Authenticate user from cookies/headers
144
+ * const token = req.headers.get('authorization')
145
+ * const user = await verifyToken(token)
146
+ *
147
+ * return {
148
+ * embed: { userId: user.id, role: user.role },
149
+ * onDisconnect: () => {
150
+ * console.log(`User ${user.id} disconnected`)
151
+ * }
152
+ * }
153
+ * }
154
+ * })
155
+ *
156
+ * @example
157
+ * // With file transfer options
158
+ * const ape = apeBun({
159
+ * where: 'api',
160
+ * fileTransferOptions: {
161
+ * startTimeout: 30000,
162
+ * completeTimeout: 60000
163
+ * }
164
+ * })
29
165
  */
30
166
  function apeBun({ where, onConnect, fileTransferOptions }) {
31
- const controllers = loader(where)
32
- const fileTransfer = getFileTransferManager(fileTransferOptions)
33
- const wss = new BunWebSocketServer({ noServer: true })
167
+ /**
168
+ * Load controllers from the specified directory.
169
+ * @type {Object<string, Function>}
170
+ */
171
+ const controllers = loader(where);
34
172
 
35
- const wsPath = `/${where}/ape`
36
- const wiringHandler = wiring(controllers, onConnect, fileTransfer)
173
+ /**
174
+ * File transfer manager for handling binary uploads/downloads.
175
+ * @type {import('./fileTransfer').FileTransferManager}
176
+ */
177
+ const fileTransfer = getFileTransferManager(fileTransferOptions);
37
178
 
38
- // Handle connections
39
- wss.on('connection', wiringHandler)
179
+ /**
180
+ * WebSocket server instance with Bun adapter.
181
+ * @type {BunWebSocketServer}
182
+ */
183
+ const wss = new BunWebSocketServer({ noServer: true });
40
184
 
41
- /**
42
- * Bun fetch handler - handles HTTP requests and WebSocket upgrades
43
- */
44
- function fetch(req, server) {
45
- const url = new URL(req.url)
46
- const pathname = url.pathname
47
-
48
- // WebSocket upgrade
49
- if (pathname === wsPath) {
50
- const upgrade = req.headers.get('upgrade')
51
- if (upgrade?.toLowerCase() === 'websocket') {
52
- // Use Bun's native upgrade
53
- const success = server.upgrade(req, {
54
- data: { req }
55
- })
56
- if (success) {
57
- return undefined // Bun handles the response
58
- }
59
- return new Response('WebSocket upgrade failed', { status: 500 })
60
- }
61
- }
185
+ /**
186
+ * WebSocket path for api-ape connections.
187
+ * @type {string}
188
+ */
189
+ const wsPath = `/${where}/ape`;
62
190
 
63
- // Serve client bundle
64
- if (pathname === `/${where}/ape.js`) {
65
- try {
66
- const filePath = path.join(__dirname, '../../dist/ape.js')
67
- const data = fs.readFileSync(filePath)
68
- return new Response(data, {
69
- headers: { 'Content-Type': 'application/javascript' }
70
- })
71
- } catch {
72
- return new Response('Client bundle not found', { status: 500 })
73
- }
191
+ /**
192
+ * Wiring handler that processes WebSocket connections and messages.
193
+ * @type {Function}
194
+ */
195
+ const wiringHandler = wiring(controllers, onConnect, fileTransfer);
196
+
197
+ // Handle WebSocket connections through the wiring layer
198
+ wss.on("connection", wiringHandler);
199
+
200
+ /**
201
+ * Bun fetch handler for HTTP requests and WebSocket upgrades.
202
+ *
203
+ * Routes handled:
204
+ * - `/{where}/ape` - WebSocket upgrade endpoint
205
+ * - `/{where}/ape.js` - Client JavaScript bundle
206
+ *
207
+ * @function fetch
208
+ * @param {Request} req - Bun Request object
209
+ * @param {Object} server - Bun server instance with upgrade() method
210
+ * @returns {Response|null|undefined} Response for handled routes, null/undefined for others
211
+ *
212
+ * @example
213
+ * // In Bun.serve()
214
+ * fetch(req, server) {
215
+ * const apeResponse = ape.fetch(req, server)
216
+ * if (apeResponse !== null) return apeResponse
217
+ * // ... handle other routes
218
+ * }
219
+ */
220
+ function fetch(req, server) {
221
+ const url = new URL(req.url);
222
+ const pathname = url.pathname;
223
+
224
+ // Handle WebSocket upgrade requests
225
+ if (pathname === wsPath) {
226
+ const upgrade = req.headers.get("upgrade");
227
+ if (upgrade?.toLowerCase() === "websocket") {
228
+ // Use Bun's native upgrade mechanism
229
+ // Store request in data for access in websocket.open
230
+ const success = server.upgrade(req, {
231
+ data: { req },
232
+ });
233
+ if (success) {
234
+ // Return undefined to signal Bun handles the response
235
+ return undefined;
74
236
  }
237
+ return new Response("WebSocket upgrade failed", { status: 500 });
238
+ }
239
+ }
75
240
 
76
- // Not an api-ape route
77
- return null
241
+ // Serve the client JavaScript bundle
242
+ if (pathname === `/${where}/ape.js`) {
243
+ try {
244
+ const filePath = path.join(__dirname, "../../dist/ape.js");
245
+ const data = fs.readFileSync(filePath);
246
+ return new Response(data, {
247
+ headers: { "Content-Type": "application/javascript" },
248
+ });
249
+ } catch {
250
+ return new Response("Client bundle not found", { status: 500 });
251
+ }
78
252
  }
79
253
 
254
+ // Not an api-ape route - return null to allow fallthrough
255
+ return null;
256
+ }
257
+
258
+ /**
259
+ * Bun WebSocket event handlers.
260
+ *
261
+ * These handlers are passed directly to Bun.serve() and handle
262
+ * the WebSocket lifecycle events. Each handler wraps Bun's native
263
+ * WebSocket in a ws-compatible BunWebSocket adapter.
264
+ *
265
+ * @type {BunWebSocketHandlers}
266
+ */
267
+ const websocket = {
80
268
  /**
81
- * Bun websocket handlers
269
+ * Called when a WebSocket connection is opened.
270
+ * Creates a BunWebSocket wrapper and passes to the wiring handler.
271
+ *
272
+ * @param {Object} ws - Bun's native WebSocket instance
82
273
  */
83
- const websocket = {
84
- open(ws) {
85
- const wrapper = new BunWebSocket(ws)
86
- wss._clients.set(ws, wrapper)
87
-
88
- const { req } = ws.data || {}
89
- wiringHandler(wrapper, req)
90
- },
91
-
92
- message(ws, message) {
93
- const wrapper = wss._clients.get(ws)
94
- if (wrapper) {
95
- wrapper._onMessage(message)
96
- }
97
- },
98
-
99
- close(ws, code, reason) {
100
- const wrapper = wss._clients.get(ws)
101
- if (wrapper) {
102
- wrapper._onClose(code, reason)
103
- wss._clients.delete(ws)
104
- }
105
- },
106
-
107
- error(ws, error) {
108
- const wrapper = wss._clients.get(ws)
109
- if (wrapper) {
110
- wrapper._onError(error)
111
- }
112
- }
113
- }
274
+ open(ws) {
275
+ // Create ws-compatible wrapper around Bun's WebSocket
276
+ const wrapper = new BunWebSocket(ws);
277
+ wss._clients.set(ws, wrapper);
114
278
 
115
- return {
116
- fetch,
117
- websocket,
118
- wss
119
- }
279
+ // Get the original request from upgrade data
280
+ const { req } = ws.data || {};
281
+
282
+ // Pass to wiring handler for api-ape processing
283
+ wiringHandler(wrapper, req);
284
+ },
285
+
286
+ /**
287
+ * Called when a message is received on the WebSocket.
288
+ * Routes the message through the wrapper's event system.
289
+ *
290
+ * @param {Object} ws - Bun's native WebSocket instance
291
+ * @param {string|Buffer} message - The received message
292
+ */
293
+ message(ws, message) {
294
+ const wrapper = wss._clients.get(ws);
295
+ if (wrapper) {
296
+ wrapper._onMessage(message);
297
+ }
298
+ },
299
+
300
+ /**
301
+ * Called when a WebSocket connection is closed.
302
+ * Triggers close event and cleans up the wrapper.
303
+ *
304
+ * @param {Object} ws - Bun's native WebSocket instance
305
+ * @param {number} code - Close status code
306
+ * @param {string} reason - Close reason
307
+ */
308
+ close(ws, code, reason) {
309
+ const wrapper = wss._clients.get(ws);
310
+ if (wrapper) {
311
+ wrapper._onClose(code, reason);
312
+ wss._clients.delete(ws);
313
+ }
314
+ },
315
+
316
+ /**
317
+ * Called when an error occurs on the WebSocket.
318
+ * Routes the error through the wrapper's event system.
319
+ *
320
+ * @param {Object} ws - Bun's native WebSocket instance
321
+ * @param {Error} error - The error that occurred
322
+ */
323
+ error(ws, error) {
324
+ const wrapper = wss._clients.get(ws);
325
+ if (wrapper) {
326
+ wrapper._onError(error);
327
+ }
328
+ },
329
+ };
330
+
331
+ return {
332
+ fetch,
333
+ websocket,
334
+ wss,
335
+ };
120
336
  }
121
337
 
122
- module.exports = { apeBun }
338
+ module.exports = { apeBun };
@@ -0,0 +1,63 @@
1
+ # File Transfer Module
2
+
3
+ ## Overview
4
+
5
+ The fileTransfer module handles binary data transfers between clients and the server. It enables api-ape to seamlessly transmit files, images, and other binary content alongside JSON messages through a tag-based system that automatically coordinates HTTP uploads and downloads.
6
+
7
+ **Key capabilities:**
8
+
9
+ - **Tagged binary fields** — Mark message fields with `<!B>` or `<!A>` to indicate pending binary uploads
10
+ - **Streaming transfers** — Client-to-client file streaming without storing complete files in memory
11
+ - **Download links** — Server can return `<!L>` tags that clients automatically fetch
12
+ - **Session verification** — All transfers are authenticated against the client's session
13
+ - **Timeout management** — Configurable timeouts for upload start and completion
14
+ - **Progress tracking** — Track streaming transfer progress with partial reads
15
+
16
+ The module works transparently—controllers receive and return regular Buffers while the framework handles the HTTP upload/download coordination behind the scenes.
17
+
18
+ > **Contributing?** See [`files.md`](./files.md) for directory structure and file descriptions.
19
+
20
+ ## Tag System
21
+
22
+ Messages can include special tags to handle binary data:
23
+
24
+ | Tag | Direction | Description |
25
+ |-----|-----------|-------------|
26
+ | `<!B>` | Client → Server | Client will upload a Buffer via HTTP PUT |
27
+ | `<!A>` | Client → Server | Client will upload an ArrayBuffer via HTTP PUT |
28
+ | `<!F>` | Client → Server | Client-to-client streaming file transfer |
29
+ | `<!L>` | Server → Client | Server returns download link for client to fetch |
30
+
31
+ ## How It Works
32
+
33
+ ```
34
+ ┌──────────────────────────────────────────────────────────────────┐
35
+ │ Binary Upload Flow │
36
+ ├──────────────────────────────────────────────────────────────────┤
37
+ │ │
38
+ │ 1. Client sends: { "image<!B>": "hash123", name: "photo.jpg" } │
39
+ │ 2. Server holds message, waits for binary upload │
40
+ │ 3. Client uploads binary via PUT /api/ape/data/qid/hash123 │
41
+ │ 4. Server injects Buffer into message: { image: <Buffer>, ... } │
42
+ │ 5. Controller receives complete message with binary data │
43
+ │ │
44
+ └──────────────────────────────────────────────────────────────────┘
45
+
46
+ ┌──────────────────────────────────────────────────────────────────┐
47
+ │ Binary Download Flow │
48
+ ├──────────────────────────────────────────────────────────────────┤
49
+ │ │
50
+ │ 1. Controller returns: { image: <Buffer>, name: "photo.jpg" } │
51
+ │ 2. Server replaces Buffer with link: { "image<!L>": "hash456" } │
52
+ │ 3. Client receives message, detects <!L> tag │
53
+ │ 4. Client fetches binary via GET /api/ape/data/hash456 │
54
+ │ 5. Client receives: { image: <ArrayBuffer>, name: "photo.jpg" } │
55
+ │ │
56
+ └──────────────────────────────────────────────────────────────────┘
57
+ ```
58
+
59
+ ## See Also
60
+
61
+ - [`../fileTransfer.js`](../fileTransfer.js) — Main file transfer manager
62
+ - [`../../socket/tagUtils.js`](../../socket/tagUtils.js) — Tag parsing utilities
63
+ - [`../../socket/receive.js`](../../socket/receive.js) — Message handler with upload coordination
@@ -0,0 +1,30 @@
1
+ # File Transfer Module Files
2
+
3
+ This module handles binary data transfers between clients and the server. It enables api-ape to seamlessly transmit files, images, and other binary content alongside JSON messages through a tag-based system that automatically coordinates HTTP uploads and downloads.
4
+
5
+ ## Guidelines
6
+
7
+ - **Tag system consistency** — Use the established tags (`<!B>`, `<!A>`, `<!L>`, `<!F>`); don't invent new ones without updating all consumers
8
+ - **Session verification** — All transfers must authenticate against the client's session; never allow unauthenticated binary access
9
+ - **Timeout handling** — Always set timeouts for pending uploads; clean up resources when timeouts expire
10
+ - **Memory management** — Streaming transfers should not load entire files into memory; use chunked processing
11
+ - **Coordinate with socket module** — Binary tag detection happens in `socket/tagUtils.js`; upload injection in `socket/receive.js`
12
+
13
+ ## Directory Structure
14
+
15
+ ```
16
+ fileTransfer/
17
+ └── streaming.js # Client-to-client file streaming manager
18
+ ```
19
+
20
+ ## Files
21
+
22
+ ### `streaming.js`
23
+
24
+ The `StreamingFileManager` class handles streaming file transfers between clients:
25
+
26
+ - **Chunked uploads** — Data arrives in pieces and is accumulated in memory
27
+ - **Partial reads** — Downloaders can read data as it arrives (progressive download)
28
+ - **Completion tracking** — Know when the full file has been received
29
+ - **Automatic cleanup** — Files are removed after configurable timeouts
30
+ - **Transfer coordination** — Manages the handoff between uploader and downloader clients