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.
Files changed (186) hide show
  1. package/README.md +59 -572
  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 -203
  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 +31 -6
  44. package/server/README.md +272 -67
  45. package/server/adapters/README.md +23 -14
  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 +322 -71
  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 -219
  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 -224
  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 -311
  186. 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";