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.
- package/README.md +58 -570
- 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 -202
- package/dist/ape.js +6 -1
- package/dist/ape.js.map +4 -4
- package/index.d.ts +38 -16
- package/package.json +32 -7
- package/server/README.md +287 -53
- package/server/adapters/README.md +28 -19
- 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 +332 -27
- 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 -221
- 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 -225
- 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 -308
- package/server/lib/broadcast.js +0 -146
package/server/lib/bun.js
CHANGED
|
@@ -1,122 +1,338 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bun-
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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({
|
|
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
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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(
|
|
20
|
-
const wiring = require(
|
|
21
|
-
const path = require(
|
|
22
|
-
const fs = require(
|
|
23
|
-
const { getFileTransferManager } = require(
|
|
24
|
-
const { BunWebSocket, BunWebSocketServer } = require(
|
|
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
|
-
*
|
|
28
|
-
*
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Load controllers from the specified directory.
|
|
169
|
+
* @type {Object<string, Function>}
|
|
170
|
+
*/
|
|
171
|
+
const controllers = loader(where);
|
|
34
172
|
|
|
35
|
-
|
|
36
|
-
|
|
173
|
+
/**
|
|
174
|
+
* File transfer manager for handling binary uploads/downloads.
|
|
175
|
+
* @type {import('./fileTransfer').FileTransferManager}
|
|
176
|
+
*/
|
|
177
|
+
const fileTransfer = getFileTransferManager(fileTransferOptions);
|
|
37
178
|
|
|
38
|
-
|
|
39
|
-
|
|
179
|
+
/**
|
|
180
|
+
* WebSocket server instance with Bun adapter.
|
|
181
|
+
* @type {BunWebSocketServer}
|
|
182
|
+
*/
|
|
183
|
+
const wss = new BunWebSocketServer({ noServer: true });
|
|
40
184
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|