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
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Binary File Upload Utilities for api-ape Client
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for processing and uploading binary data
|
|
5
|
+
* (ArrayBuffer, TypedArray, Blob) in api-ape messages. Binary data is extracted
|
|
6
|
+
* from payloads and uploaded via HTTP, with the original payload modified to
|
|
7
|
+
* contain reference hashes.
|
|
8
|
+
*
|
|
9
|
+
* ## Binary Data Flow (Upload)
|
|
10
|
+
*
|
|
11
|
+
* ```
|
|
12
|
+
* Original Payload Processed Payload
|
|
13
|
+
* ───────────────── ──────────────────
|
|
14
|
+
* { {
|
|
15
|
+
* name: 'doc.pdf', name: 'doc.pdf',
|
|
16
|
+
* file: ArrayBuffer(...) → 'file<!A>': 'abc123'
|
|
17
|
+
* } }
|
|
18
|
+
* │
|
|
19
|
+
* ▼
|
|
20
|
+
* HTTP PUT /api/ape/data/{queryId}/abc123
|
|
21
|
+
* │
|
|
22
|
+
* ▼
|
|
23
|
+
* Server receives binary + reference
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* ## Tag System
|
|
27
|
+
*
|
|
28
|
+
* Binary references use a tag system in property keys:
|
|
29
|
+
* - `<!A>` - ArrayBuffer or TypedArray upload
|
|
30
|
+
* - `<!B>` - Blob upload
|
|
31
|
+
* - `<!F>` - File sharing (client-to-client)
|
|
32
|
+
*
|
|
33
|
+
* ## Two Upload Modes
|
|
34
|
+
*
|
|
35
|
+
* 1. **Standard Upload** (`processBinaryForUpload`) - For sending binary data
|
|
36
|
+
* from client to server as part of a request
|
|
37
|
+
*
|
|
38
|
+
* 2. **File Sharing** (`processBinaryForSharing`) - For client-to-client
|
|
39
|
+
* binary transfers where data is stored temporarily on server
|
|
40
|
+
*
|
|
41
|
+
* @module client/connection/fileHandling
|
|
42
|
+
* @see {@link module:client/connection/fileDownload} for downloading binary data
|
|
43
|
+
* @see {@link module:client/connection/fileUtils} for shared utilities
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* // Upload binary data with a message
|
|
47
|
+
* import { processBinaryForUpload, uploadBinaryData } from './fileHandling'
|
|
48
|
+
*
|
|
49
|
+
* const payload = {
|
|
50
|
+
* name: 'photo.jpg',
|
|
51
|
+
* image: myArrayBuffer
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* const { processedData, uploads } = processBinaryForUpload(payload)
|
|
55
|
+
* // processedData = { name: 'photo.jpg', 'image<!A>': 'hashXYZ' }
|
|
56
|
+
*
|
|
57
|
+
* // Upload binary data separately
|
|
58
|
+
* await uploadBinaryData(queryId, uploads)
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* // Share files between clients
|
|
62
|
+
* import { processBinaryForSharing, uploadSharedFiles } from './fileHandling'
|
|
63
|
+
*
|
|
64
|
+
* const { processedData, shares } = processBinaryForSharing({
|
|
65
|
+
* screenshot: screenshotBlob
|
|
66
|
+
* })
|
|
67
|
+
*
|
|
68
|
+
* await uploadSharedFiles(shares)
|
|
69
|
+
* // Other clients can now fetch the shared file
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
import { getBaseUrl } from "./network";
|
|
73
|
+
import { isBinaryData, getBinaryTag, generateUploadHash } from "./fileUtils";
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Process a data payload and extract binary data for HTTP upload
|
|
77
|
+
*
|
|
78
|
+
* Recursively traverses the data object, finding any binary data
|
|
79
|
+
* (ArrayBuffer, TypedArray, Blob) and replacing it with a tagged
|
|
80
|
+
* reference hash. The binary data is collected for separate HTTP upload.
|
|
81
|
+
*
|
|
82
|
+
* ## Processing Logic
|
|
83
|
+
*
|
|
84
|
+
* - Primitive values: Passed through unchanged
|
|
85
|
+
* - Binary data: Replaced with `{ __ape_upload__: hash }` and added to uploads
|
|
86
|
+
* - Arrays: Each element processed recursively
|
|
87
|
+
* - Objects: Each property processed recursively, binary keys get tagged
|
|
88
|
+
*
|
|
89
|
+
* @param {any} data - The payload data to process
|
|
90
|
+
* @param {string} [path=''] - Current path in the object tree (for hash generation)
|
|
91
|
+
* @returns {{processedData: any, uploads: Array<{path: string, hash: string, data: any, tag: string}>}}
|
|
92
|
+
* Object containing the processed data and array of binary uploads
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* // Simple binary property
|
|
96
|
+
* const { processedData, uploads } = processBinaryForUpload({
|
|
97
|
+
* name: 'file.bin',
|
|
98
|
+
* content: new ArrayBuffer(1024)
|
|
99
|
+
* })
|
|
100
|
+
*
|
|
101
|
+
* console.log(processedData)
|
|
102
|
+
* // { name: 'file.bin', 'content<!A>': '7xyz3' }
|
|
103
|
+
*
|
|
104
|
+
* console.log(uploads)
|
|
105
|
+
* // [{ path: 'content', hash: '7xyz3', data: ArrayBuffer, tag: 'A' }]
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* // Nested binary data
|
|
109
|
+
* const { processedData, uploads } = processBinaryForUpload({
|
|
110
|
+
* files: [
|
|
111
|
+
* { name: 'a.png', data: buffer1 },
|
|
112
|
+
* { name: 'b.png', data: buffer2 }
|
|
113
|
+
* ]
|
|
114
|
+
* })
|
|
115
|
+
*
|
|
116
|
+
* console.log(processedData)
|
|
117
|
+
* // {
|
|
118
|
+
* // files: [
|
|
119
|
+
* // { name: 'a.png', 'data<!A>': 'hash1' },
|
|
120
|
+
* // { name: 'b.png', 'data<!A>': 'hash2' }
|
|
121
|
+
* // ]
|
|
122
|
+
* // }
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* // No binary data - passthrough
|
|
126
|
+
* const { processedData, uploads } = processBinaryForUpload({
|
|
127
|
+
* message: 'Hello',
|
|
128
|
+
* count: 42
|
|
129
|
+
* })
|
|
130
|
+
*
|
|
131
|
+
* console.log(processedData)
|
|
132
|
+
* // { message: 'Hello', count: 42 }
|
|
133
|
+
*
|
|
134
|
+
* console.log(uploads)
|
|
135
|
+
* // []
|
|
136
|
+
*/
|
|
137
|
+
export function processBinaryForUpload(data, path = "") {
|
|
138
|
+
// Handle null/undefined
|
|
139
|
+
if (data === null || data === undefined) {
|
|
140
|
+
return { processedData: data, uploads: [] };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Handle binary data - extract and replace with hash reference
|
|
144
|
+
if (isBinaryData(data)) {
|
|
145
|
+
const tag = getBinaryTag(data);
|
|
146
|
+
const hash = generateUploadHash(path || "root");
|
|
147
|
+
return {
|
|
148
|
+
processedData: { [`__ape_upload__`]: hash },
|
|
149
|
+
uploads: [{ path, hash, data, tag }],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Handle arrays - process each element recursively
|
|
154
|
+
if (Array.isArray(data)) {
|
|
155
|
+
const processedArray = [];
|
|
156
|
+
const allUploads = [];
|
|
157
|
+
|
|
158
|
+
for (let i = 0; i < data.length; i++) {
|
|
159
|
+
const itemPath = path ? `${path}.${i}` : String(i);
|
|
160
|
+
const { processedData, uploads } = processBinaryForUpload(
|
|
161
|
+
data[i],
|
|
162
|
+
itemPath,
|
|
163
|
+
);
|
|
164
|
+
processedArray.push(processedData);
|
|
165
|
+
allUploads.push(...uploads);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return { processedData: processedArray, uploads: allUploads };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Handle objects - process each property recursively
|
|
172
|
+
if (typeof data === "object") {
|
|
173
|
+
const processedObj = {};
|
|
174
|
+
const allUploads = [];
|
|
175
|
+
|
|
176
|
+
for (const key of Object.keys(data)) {
|
|
177
|
+
const itemPath = path ? `${path}.${key}` : key;
|
|
178
|
+
const { processedData, uploads } = processBinaryForUpload(
|
|
179
|
+
data[key],
|
|
180
|
+
itemPath,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
// If this property contained binary data, add tag to the key
|
|
184
|
+
if (uploads.length > 0 && processedData?.__ape_upload__) {
|
|
185
|
+
const tag = uploads[uploads.length - 1].tag;
|
|
186
|
+
processedObj[`${key}<!${tag}>`] = processedData.__ape_upload__;
|
|
187
|
+
} else {
|
|
188
|
+
processedObj[key] = processedData;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
allUploads.push(...uploads);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return { processedData: processedObj, uploads: allUploads };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Primitive values - return as-is
|
|
198
|
+
return { processedData: data, uploads: [] };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Process a data payload and extract binary data for client-to-client sharing
|
|
203
|
+
*
|
|
204
|
+
* Similar to `processBinaryForUpload`, but uses the `<!F>` tag for file sharing.
|
|
205
|
+
* Shared files are uploaded to a temporary storage endpoint and can be fetched
|
|
206
|
+
* by other clients using the hash reference.
|
|
207
|
+
*
|
|
208
|
+
* ## Difference from Standard Upload
|
|
209
|
+
*
|
|
210
|
+
* - Standard uploads are tied to a specific request/query
|
|
211
|
+
* - Shared files are stored with a content-addressable hash
|
|
212
|
+
* - Shared files can be fetched by any client that knows the hash
|
|
213
|
+
*
|
|
214
|
+
* @param {any} data - The payload data to process
|
|
215
|
+
* @param {string} [path=''] - Current path in the object tree (for hash generation)
|
|
216
|
+
* @returns {{processedData: any, shares: Array<{path: string, hash: string, data: any}>}}
|
|
217
|
+
* Object containing the processed data and array of files to share
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* // Share a screenshot with other clients
|
|
221
|
+
* const { processedData, shares } = processBinaryForSharing({
|
|
222
|
+
* type: 'screenshot',
|
|
223
|
+
* image: screenshotArrayBuffer,
|
|
224
|
+
* timestamp: Date.now()
|
|
225
|
+
* })
|
|
226
|
+
*
|
|
227
|
+
* console.log(processedData)
|
|
228
|
+
* // { type: 'screenshot', 'image<!F>': 'shareHash123', timestamp: 1699999999999 }
|
|
229
|
+
*
|
|
230
|
+
* // Upload the shared file
|
|
231
|
+
* await uploadSharedFiles(shares)
|
|
232
|
+
*
|
|
233
|
+
* // Now broadcast to other clients who can fetch the image
|
|
234
|
+
* broadcast('screenshot', processedData)
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* // Multiple shared files
|
|
238
|
+
* const { processedData, shares } = processBinaryForSharing({
|
|
239
|
+
* attachments: [
|
|
240
|
+
* { name: 'doc1.pdf', content: pdfBuffer1 },
|
|
241
|
+
* { name: 'doc2.pdf', content: pdfBuffer2 }
|
|
242
|
+
* ]
|
|
243
|
+
* })
|
|
244
|
+
*
|
|
245
|
+
* await uploadSharedFiles(shares)
|
|
246
|
+
*/
|
|
247
|
+
export function processBinaryForSharing(data, path = "") {
|
|
248
|
+
// Handle null/undefined
|
|
249
|
+
if (data === null || data === undefined) {
|
|
250
|
+
return { processedData: data, shares: [] };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Handle binary data - extract and replace with share hash
|
|
254
|
+
if (isBinaryData(data)) {
|
|
255
|
+
const hash = generateUploadHash(path || "share");
|
|
256
|
+
return {
|
|
257
|
+
processedData: { [`__ape_share__`]: hash },
|
|
258
|
+
shares: [{ path, hash, data }],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Handle arrays - process each element recursively
|
|
263
|
+
if (Array.isArray(data)) {
|
|
264
|
+
const processedArray = [];
|
|
265
|
+
const allShares = [];
|
|
266
|
+
|
|
267
|
+
for (let i = 0; i < data.length; i++) {
|
|
268
|
+
const itemPath = path ? `${path}.${i}` : String(i);
|
|
269
|
+
const { processedData, shares } = processBinaryForSharing(
|
|
270
|
+
data[i],
|
|
271
|
+
itemPath,
|
|
272
|
+
);
|
|
273
|
+
processedArray.push(processedData);
|
|
274
|
+
allShares.push(...shares);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return { processedData: processedArray, shares: allShares };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Handle objects - process each property recursively
|
|
281
|
+
if (typeof data === "object") {
|
|
282
|
+
const processedObj = {};
|
|
283
|
+
const allShares = [];
|
|
284
|
+
|
|
285
|
+
for (const key of Object.keys(data)) {
|
|
286
|
+
const itemPath = path ? `${path}.${key}` : key;
|
|
287
|
+
const { processedData, shares } = processBinaryForSharing(
|
|
288
|
+
data[key],
|
|
289
|
+
itemPath,
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// If this property contained binary data, add F tag to the key
|
|
293
|
+
if (shares.length > 0 && processedData?.__ape_share__) {
|
|
294
|
+
processedObj[`${key}<!F>`] = processedData.__ape_share__;
|
|
295
|
+
} else {
|
|
296
|
+
processedObj[key] = processedData;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
allShares.push(...shares);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return { processedData: processedObj, shares: allShares };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Primitive values - return as-is
|
|
306
|
+
return { processedData: data, shares: [] };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Upload binary data via HTTP PUT requests
|
|
311
|
+
*
|
|
312
|
+
* Takes the uploads array from `processBinaryForUpload` and sends each
|
|
313
|
+
* binary payload to the server via HTTP PUT. The uploads are performed
|
|
314
|
+
* in parallel for efficiency.
|
|
315
|
+
*
|
|
316
|
+
* ## Upload Endpoint
|
|
317
|
+
*
|
|
318
|
+
* Binary data is uploaded to: `PUT /api/ape/data/{queryId}/{hash}`
|
|
319
|
+
*
|
|
320
|
+
* The server matches the upload to the original WebSocket message using
|
|
321
|
+
* the queryId, and associates the binary data with the correct property
|
|
322
|
+
* using the hash.
|
|
323
|
+
*
|
|
324
|
+
* @param {string} queryId - The query ID of the associated WebSocket message
|
|
325
|
+
* @param {Array<{hash: string, data: ArrayBuffer|Blob|TypedArray}>} uploads - Array of upload objects
|
|
326
|
+
* @returns {Promise<void>} Resolves when all uploads complete
|
|
327
|
+
* @throws {Error} If any upload fails
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* // Standard usage with processBinaryForUpload
|
|
331
|
+
* const { processedData, uploads } = processBinaryForUpload(payload)
|
|
332
|
+
*
|
|
333
|
+
* if (uploads.length > 0) {
|
|
334
|
+
* await uploadBinaryData(queryId, uploads)
|
|
335
|
+
* }
|
|
336
|
+
*
|
|
337
|
+
* // Send the processed message via WebSocket
|
|
338
|
+
* ws.send(JSON.stringify({ queryId, data: processedData }))
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* // Error handling
|
|
342
|
+
* try {
|
|
343
|
+
* await uploadBinaryData(queryId, uploads)
|
|
344
|
+
* } catch (err) {
|
|
345
|
+
* console.error('Binary upload failed:', err)
|
|
346
|
+
* // Handle failure - maybe retry or notify user
|
|
347
|
+
* }
|
|
348
|
+
*/
|
|
349
|
+
export async function uploadBinaryData(queryId, uploads) {
|
|
350
|
+
if (uploads.length === 0) return;
|
|
351
|
+
|
|
352
|
+
const baseUrl = getBaseUrl();
|
|
353
|
+
|
|
354
|
+
await Promise.all(
|
|
355
|
+
uploads.map(async ({ hash, data }) => {
|
|
356
|
+
const response = await fetch(
|
|
357
|
+
`${baseUrl}/api/ape/data/${queryId}/${hash}`,
|
|
358
|
+
{
|
|
359
|
+
method: "PUT",
|
|
360
|
+
credentials: "include",
|
|
361
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
362
|
+
body: data,
|
|
363
|
+
},
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
if (!response.ok) {
|
|
367
|
+
throw new Error(`Upload failed: ${response.status}`);
|
|
368
|
+
}
|
|
369
|
+
}),
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Upload shared files via HTTP PUT for client-to-client transfer
|
|
375
|
+
*
|
|
376
|
+
* Takes the shares array from `processBinaryForSharing` and uploads each
|
|
377
|
+
* file to the server's shared file storage. Other clients can then fetch
|
|
378
|
+
* these files using the hash reference.
|
|
379
|
+
*
|
|
380
|
+
* ## Share Endpoint
|
|
381
|
+
*
|
|
382
|
+
* Shared files are uploaded to: `PUT /api/ape/data/_share/{hash}`
|
|
383
|
+
*
|
|
384
|
+
* The `_share` path indicates this is for client-to-client sharing rather
|
|
385
|
+
* than a request-specific upload.
|
|
386
|
+
*
|
|
387
|
+
* ## Temporary Storage
|
|
388
|
+
*
|
|
389
|
+
* Shared files are stored temporarily on the server and will be cleaned up
|
|
390
|
+
* after a configurable timeout (default: 60 seconds for start, 60 seconds
|
|
391
|
+
* after first access).
|
|
392
|
+
*
|
|
393
|
+
* @param {Array<{hash: string, data: ArrayBuffer|Blob|TypedArray}>} shares - Array of share objects
|
|
394
|
+
* @returns {Promise<void>} Resolves when all uploads complete
|
|
395
|
+
* @throws {Error} If any upload fails
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* // Share files with other clients
|
|
399
|
+
* const { processedData, shares } = processBinaryForSharing({
|
|
400
|
+
* image: imageBuffer
|
|
401
|
+
* })
|
|
402
|
+
*
|
|
403
|
+
* await uploadSharedFiles(shares)
|
|
404
|
+
*
|
|
405
|
+
* // Broadcast to other clients
|
|
406
|
+
* broadcast('shared-image', processedData)
|
|
407
|
+
* // Other clients will receive: { 'image<!F>': 'hashXYZ' }
|
|
408
|
+
* // They can fetch it via: GET /api/ape/data/hashXYZ
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* // Batch upload multiple files
|
|
412
|
+
* const shares = [
|
|
413
|
+
* { hash: 'hash1', data: buffer1 },
|
|
414
|
+
* { hash: 'hash2', data: buffer2 },
|
|
415
|
+
* { hash: 'hash3', data: buffer3 }
|
|
416
|
+
* ]
|
|
417
|
+
*
|
|
418
|
+
* // All uploads happen in parallel
|
|
419
|
+
* await uploadSharedFiles(shares)
|
|
420
|
+
*/
|
|
421
|
+
export async function uploadSharedFiles(shares) {
|
|
422
|
+
if (shares.length === 0) return;
|
|
423
|
+
|
|
424
|
+
const baseUrl = getBaseUrl();
|
|
425
|
+
|
|
426
|
+
await Promise.all(
|
|
427
|
+
shares.map(async ({ hash, data }) => {
|
|
428
|
+
const response = await fetch(`${baseUrl}/api/ape/data/_share/${hash}`, {
|
|
429
|
+
method: "PUT",
|
|
430
|
+
credentials: "include",
|
|
431
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
432
|
+
body: data,
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
if (!response.ok) {
|
|
436
|
+
throw new Error(`Shared upload failed: ${response.status}`);
|
|
437
|
+
}
|
|
438
|
+
}),
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Re-export setValueAtPath for fileDownload module
|
|
444
|
+
*
|
|
445
|
+
* This utility is used by the download module to place fetched binary
|
|
446
|
+
* data back into the correct location in the data object.
|
|
447
|
+
*
|
|
448
|
+
* @see {@link module:client/connection/fileUtils.setValueAtPath}
|
|
449
|
+
*/
|
|
450
|
+
export { setValueAtPath } from "./fileUtils";
|