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
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Client Connection Module
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The connection module handles the core mechanics of maintaining a WebSocket connection to an api-ape server. It manages the full lifecycle of client-server communication including connection state, message sending, request/response correlation, and binary file transfers.
|
|
6
|
+
|
|
7
|
+
This module powers the seamless API experience where `api.users.list()` transparently becomes a WebSocket message, waits for the response, and returns the result as a Promise—all while handling reconnection, queuing, and binary data automatically.
|
|
8
|
+
|
|
9
|
+
**Key capabilities:**
|
|
10
|
+
|
|
11
|
+
- **Connection state management** — Track and expose connection status (offline, connecting, connected, disconnected, walled)
|
|
12
|
+
- **Proxy API generation** — Convert `api.path.method()` calls into WebSocket messages
|
|
13
|
+
- **Request correlation** — Match responses to requests via `queryId`
|
|
14
|
+
- **Network detection** — Detect offline state, captive portals, and connectivity changes
|
|
15
|
+
- **File transfers** — Handle binary uploads and downloads transparently
|
|
16
|
+
|
|
17
|
+
> **Contributing?** See [`files.md`](./files.md) for directory structure and file descriptions.
|
|
18
|
+
|
|
19
|
+
## See Also
|
|
20
|
+
|
|
21
|
+
- [`../README.md`](../README.md) — Client module overview
|
|
22
|
+
- [`../transports/README.md`](../transports/README.md) — HTTP fallback transport
|
|
23
|
+
- [`../connectSocket.js`](../connectSocket.js) — Main WebSocket client
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Binary file download/fetch utilities for api-ape client
|
|
3
|
+
*
|
|
4
|
+
* This module handles fetching binary data that is referenced in server responses.
|
|
5
|
+
* When the server sends binary data (like images, files, etc.), it doesn't include
|
|
6
|
+
* the raw bytes in the WebSocket message. Instead, it sends a tagged reference
|
|
7
|
+
* that this module resolves by fetching the actual data via HTTP.
|
|
8
|
+
*
|
|
9
|
+
* ## Tag Types
|
|
10
|
+
*
|
|
11
|
+
* - `<!L>` - **Linked Resource**: Binary data from server responses (server → client)
|
|
12
|
+
* - `<!F>` - **Shared File**: Binary data from other clients (client → client via server)
|
|
13
|
+
*
|
|
14
|
+
* ## Data Flow
|
|
15
|
+
*
|
|
16
|
+
* ```
|
|
17
|
+
* Server Response:
|
|
18
|
+
* { "image<!L>": "abc123", "name": "photo.jpg" }
|
|
19
|
+
*
|
|
20
|
+
* After fetchLinkedResources():
|
|
21
|
+
* { "image": ArrayBuffer(...), "name": "photo.jpg" }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* ## Retry Logic
|
|
25
|
+
*
|
|
26
|
+
* Shared files (F-tagged) use exponential backoff retry because the file
|
|
27
|
+
* might not be immediately available when the message arrives (the sender
|
|
28
|
+
* might still be uploading).
|
|
29
|
+
*
|
|
30
|
+
* @module client/connection/fileDownload
|
|
31
|
+
* @see {@link module:client/connection/fileHandling} for upload utilities
|
|
32
|
+
* @see {@link module:client/connection/fileUtils} for shared utility functions
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Hydrating a server response
|
|
36
|
+
* import { fetchLinkedResources, fetchSharedFiles } from './fileDownload'
|
|
37
|
+
*
|
|
38
|
+
* const serverData = { "avatar<!L>": "hash123", username: "alice" }
|
|
39
|
+
*
|
|
40
|
+
* // Fetch the binary data
|
|
41
|
+
* const hydrated = await fetchLinkedResources(serverData)
|
|
42
|
+
* // Result: { avatar: ArrayBuffer(...), username: "alice" }
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // Handling shared files from other clients
|
|
46
|
+
* const messageData = { "attachment<!F>": "filehash", text: "Check this out!" }
|
|
47
|
+
*
|
|
48
|
+
* // Fetch with retry logic
|
|
49
|
+
* const hydrated = await fetchSharedFiles(messageData)
|
|
50
|
+
* // Result: { attachment: ArrayBuffer(...), text: "Check this out!" }
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
import { getBaseUrl } from "./network";
|
|
54
|
+
import { setValueAtPath, findTaggedProps, cleanTaggedKeys } from "./fileUtils";
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Fetch binary resources linked from server responses
|
|
58
|
+
*
|
|
59
|
+
* This function processes data objects that contain L-tagged binary references.
|
|
60
|
+
* Each L-tagged property is replaced with the actual binary data fetched from
|
|
61
|
+
* the server's data endpoint.
|
|
62
|
+
*
|
|
63
|
+
* ## Processing Steps
|
|
64
|
+
*
|
|
65
|
+
* 1. Scan the data object for properties ending with `<!L>`
|
|
66
|
+
* 2. For each tagged property, extract the hash value
|
|
67
|
+
* 3. Fetch the binary data from `/api/ape/data/{hash}`
|
|
68
|
+
* 4. Replace the hash with the fetched ArrayBuffer
|
|
69
|
+
* 5. Rename the key to remove the `<!L>` suffix
|
|
70
|
+
*
|
|
71
|
+
* ## Error Handling
|
|
72
|
+
*
|
|
73
|
+
* If a fetch fails, the property is set to `null` rather than throwing.
|
|
74
|
+
* This prevents one failed resource from breaking the entire response.
|
|
75
|
+
*
|
|
76
|
+
* @param {Object} data - Data object potentially containing L-tagged binary references
|
|
77
|
+
* @param {string} [clientId] - Optional client ID for authentication header
|
|
78
|
+
* @returns {Promise<Object>} Hydrated data object with binary resources fetched
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* // Server sends avatar as a linked resource
|
|
82
|
+
* const serverResponse = {
|
|
83
|
+
* "profilePic<!L>": "abc123def456",
|
|
84
|
+
* "username": "alice",
|
|
85
|
+
* "bio": "Hello world!"
|
|
86
|
+
* }
|
|
87
|
+
*
|
|
88
|
+
* const hydrated = await fetchLinkedResources(serverResponse)
|
|
89
|
+
* // Result:
|
|
90
|
+
* // {
|
|
91
|
+
* // profilePic: ArrayBuffer(12345), // The actual image data
|
|
92
|
+
* // username: "alice",
|
|
93
|
+
* // bio: "Hello world!"
|
|
94
|
+
* // }
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* // Multiple binary resources
|
|
98
|
+
* const response = {
|
|
99
|
+
* "thumbnail<!L>": "hash1",
|
|
100
|
+
* "fullImage<!L>": "hash2",
|
|
101
|
+
* "metadata": { width: 1920, height: 1080 }
|
|
102
|
+
* }
|
|
103
|
+
*
|
|
104
|
+
* const hydrated = await fetchLinkedResources(response)
|
|
105
|
+
* // Both thumbnail and fullImage are fetched in parallel
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* // Nested binary resources
|
|
109
|
+
* const response = {
|
|
110
|
+
* user: {
|
|
111
|
+
* name: "Bob",
|
|
112
|
+
* "avatar<!L>": "avatarhash"
|
|
113
|
+
* },
|
|
114
|
+
* attachments: [
|
|
115
|
+
* { "file<!L>": "file1hash", name: "doc.pdf" },
|
|
116
|
+
* { "file<!L>": "file2hash", name: "image.png" }
|
|
117
|
+
* ]
|
|
118
|
+
* }
|
|
119
|
+
*
|
|
120
|
+
* const hydrated = await fetchLinkedResources(response)
|
|
121
|
+
* // All nested binary resources are fetched
|
|
122
|
+
*/
|
|
123
|
+
export async function fetchLinkedResources(data, clientId) {
|
|
124
|
+
const resources = findTaggedProps(data, "L");
|
|
125
|
+
if (resources.length === 0) return data;
|
|
126
|
+
|
|
127
|
+
console.log(`🦍 Fetching ${resources.length} binary resource(s)`);
|
|
128
|
+
const cleanedData = cleanTaggedKeys(data, "L");
|
|
129
|
+
const baseUrl = getBaseUrl();
|
|
130
|
+
|
|
131
|
+
await Promise.all(
|
|
132
|
+
resources.map(async ({ path, hash }) => {
|
|
133
|
+
try {
|
|
134
|
+
const response = await fetch(`${baseUrl}/api/ape/data/${hash}`, {
|
|
135
|
+
credentials: "include",
|
|
136
|
+
headers: { "X-Ape-Client-Id": clientId || "" },
|
|
137
|
+
});
|
|
138
|
+
if (!response.ok) throw new Error(`Failed: ${response.status}`);
|
|
139
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
140
|
+
setValueAtPath(cleanedData, path, arrayBuffer);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.error(`🦍 Failed to fetch binary resource at ${path}:`, err);
|
|
143
|
+
setValueAtPath(cleanedData, path, null);
|
|
144
|
+
}
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
return cleanedData;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Fetch shared files from client-to-client transfers
|
|
153
|
+
*
|
|
154
|
+
* This function handles F-tagged file references, which represent files
|
|
155
|
+
* shared between clients via the server. Unlike L-tagged resources, F-tagged
|
|
156
|
+
* files use retry logic because the file might not be immediately available
|
|
157
|
+
* (the sending client might still be uploading).
|
|
158
|
+
*
|
|
159
|
+
* ## Retry Behavior
|
|
160
|
+
*
|
|
161
|
+
* - Uses exponential backoff starting at 100ms
|
|
162
|
+
* - Doubles delay after each retry (100ms → 200ms → 400ms → ...)
|
|
163
|
+
* - Retries up to `maxRetries` times (default: 5)
|
|
164
|
+
* - Only retries on 404 errors (file not yet uploaded)
|
|
165
|
+
*
|
|
166
|
+
* ## Use Case
|
|
167
|
+
*
|
|
168
|
+
* Client-to-client file sharing workflow:
|
|
169
|
+
* 1. Client A sends message with file reference
|
|
170
|
+
* 2. Server broadcasts message to Client B
|
|
171
|
+
* 3. Client A uploads file to server (may take time)
|
|
172
|
+
* 4. Client B receives message, attempts to fetch file
|
|
173
|
+
* 5. If 404, retry until file is available
|
|
174
|
+
*
|
|
175
|
+
* @param {Object} data - Data object potentially containing F-tagged file references
|
|
176
|
+
* @param {number} [maxRetries=5] - Maximum number of retry attempts per file
|
|
177
|
+
* @returns {Promise<Object>} Hydrated data object with shared files fetched
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* // Receiving a shared file from another client
|
|
181
|
+
* const message = {
|
|
182
|
+
* "sharedDoc<!F>": "uniquefilehash",
|
|
183
|
+
* "sender": "alice",
|
|
184
|
+
* "text": "Here's the document you requested"
|
|
185
|
+
* }
|
|
186
|
+
*
|
|
187
|
+
* const hydrated = await fetchSharedFiles(message)
|
|
188
|
+
* // Result:
|
|
189
|
+
* // {
|
|
190
|
+
* // sharedDoc: ArrayBuffer(...), // The shared file
|
|
191
|
+
* // sender: "alice",
|
|
192
|
+
* // text: "Here's the document you requested"
|
|
193
|
+
* // }
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* // With custom retry count
|
|
197
|
+
* const hydrated = await fetchSharedFiles(data, 10) // Up to 10 retries
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* // Multiple shared files
|
|
201
|
+
* const data = {
|
|
202
|
+
* "photo<!F>": "hash1",
|
|
203
|
+
* "video<!F>": "hash2",
|
|
204
|
+
* caption: "My vacation pics!"
|
|
205
|
+
* }
|
|
206
|
+
*
|
|
207
|
+
* // Both files fetched in parallel with independent retry logic
|
|
208
|
+
* const hydrated = await fetchSharedFiles(data)
|
|
209
|
+
*/
|
|
210
|
+
export async function fetchSharedFiles(data, maxRetries = 5) {
|
|
211
|
+
const files = findTaggedProps(data, "F");
|
|
212
|
+
if (files.length === 0) return data;
|
|
213
|
+
|
|
214
|
+
console.log(`🦍 Fetching ${files.length} shared file(s)`);
|
|
215
|
+
const cleanedData = cleanTaggedKeys(data, "F");
|
|
216
|
+
const baseUrl = getBaseUrl();
|
|
217
|
+
|
|
218
|
+
await Promise.all(
|
|
219
|
+
files.map(async ({ path, hash }) => {
|
|
220
|
+
let retries = 0;
|
|
221
|
+
let backoff = 100;
|
|
222
|
+
|
|
223
|
+
while (retries < maxRetries) {
|
|
224
|
+
try {
|
|
225
|
+
const response = await fetch(`${baseUrl}/api/ape/data/${hash}`, {
|
|
226
|
+
credentials: "include",
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (!response.ok) {
|
|
230
|
+
// Retry on 404 (file not yet uploaded)
|
|
231
|
+
if (response.status === 404 && retries < maxRetries - 1) {
|
|
232
|
+
retries++;
|
|
233
|
+
await new Promise((r) => setTimeout(r, backoff));
|
|
234
|
+
backoff *= 2;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
throw new Error(`Failed to fetch shared file: ${response.status}`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
setValueAtPath(cleanedData, path, await response.arrayBuffer());
|
|
241
|
+
break;
|
|
242
|
+
} catch (err) {
|
|
243
|
+
if (retries >= maxRetries - 1) {
|
|
244
|
+
console.error(`🦍 Failed to fetch shared file at ${path}:`, err);
|
|
245
|
+
setValueAtPath(cleanedData, path, null);
|
|
246
|
+
}
|
|
247
|
+
retries++;
|
|
248
|
+
await new Promise((r) => setTimeout(r, backoff));
|
|
249
|
+
backoff *= 2;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
return cleanedData;
|
|
256
|
+
}
|