Skip to content

Commit 0713599

Browse files
authored
Update stat.js (#1753)
1 parent 5a8f06d commit 0713599

File tree

1 file changed

+101
-39
lines changed
  • src/puter-js/src/modules/FileSystem/operations

1 file changed

+101
-39
lines changed

src/puter-js/src/modules/FileSystem/operations/stat.js

Lines changed: 101 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import * as utils from '../../../lib/utils.js';
22
import getAbsolutePathForApp from '../utils/getAbsolutePathForApp.js';
33

4+
// Track in-flight requests to avoid duplicate backend calls
5+
// Each entry stores: { promise, timestamp }
6+
const inflightRequests = new Map();
7+
8+
// Time window (in ms) to group duplicate requests together
9+
// Requests made within this window will share the same backend call
10+
const DEDUPLICATION_WINDOW_MS = 2000; // 2 seconds
11+
412
const stat = async function (...args) {
513
let options;
614

@@ -24,17 +32,6 @@ const stat = async function (...args) {
2432
options.consistency = 'strong';
2533
}
2634

27-
// If auth token is not provided and we are in the web environment,
28-
// try to authenticate with Puter
29-
if(!puter.authToken && puter.env === 'web'){
30-
try{
31-
await puter.ui.authenticateWithPuter();
32-
}catch(e){
33-
// if authentication fails, throw an error
34-
reject('Authentication failed.');
35-
}
36-
}
37-
3835
// Generate cache key based on path or uid
3936
let cacheKey;
4037
if(options.path){
@@ -50,41 +47,106 @@ const stat = async function (...args) {
5047
}
5148
}
5249

53-
// create xhr object
54-
const xhr = utils.initXhr('/stat', this.APIOrigin, undefined, "post", "text/plain;actually=json");
50+
// Generate deduplication key based on all request parameters
51+
const deduplicationKey = JSON.stringify({
52+
path: options.path,
53+
uid: options.uid,
54+
returnSubdomains: options.returnSubdomains,
55+
returnPermissions: options.returnPermissions,
56+
returnVersions: options.returnVersions,
57+
returnSize: options.returnSize,
58+
consistency: options.consistency,
59+
});
5560

56-
// set up event handlers for load and error events
57-
utils.setupXhrEventHandlers(xhr, options.success, options.error, async (result) => {
58-
// Calculate the size of the result for cache eligibility check
59-
const resultSize = JSON.stringify(result).length;
61+
// Check if there's already an in-flight request for the same parameters
62+
const existingEntry = inflightRequests.get(deduplicationKey);
63+
const now = Date.now();
64+
65+
if (existingEntry) {
66+
const timeSinceRequest = now - existingEntry.timestamp;
6067

61-
// Cache the result if it's not bigger than MAX_CACHE_SIZE
62-
const MAX_CACHE_SIZE = 20 * 1024 * 1024;
68+
// Only reuse the request if it's within the deduplication window
69+
if (timeSinceRequest < DEDUPLICATION_WINDOW_MS) {
70+
// Wait for the existing request and return its result
71+
try {
72+
const result = await existingEntry.promise;
73+
resolve(result);
74+
} catch (error) {
75+
reject(error);
76+
}
77+
return;
78+
} else {
79+
// Request is too old, remove it from the tracker
80+
inflightRequests.delete(deduplicationKey);
81+
}
82+
}
6383

64-
if(resultSize <= MAX_CACHE_SIZE){
65-
// UPSERT the cache
66-
puter._cache.set(cacheKey, result);
84+
// Create a promise for this request and store it to deduplicate concurrent calls
85+
const requestPromise = new Promise(async (resolveRequest, rejectRequest) => {
86+
// If auth token is not provided and we are in the web environment,
87+
// try to authenticate with Puter
88+
if(!puter.authToken && puter.env === 'web'){
89+
try{
90+
await puter.ui.authenticateWithPuter();
91+
}catch(e){
92+
// if authentication fails, throw an error
93+
rejectRequest('Authentication failed.');
94+
return;
95+
}
96+
}
97+
98+
// create xhr object
99+
const xhr = utils.initXhr('/stat', this.APIOrigin, undefined, "post", "text/plain;actually=json");
100+
101+
// set up event handlers for load and error events
102+
utils.setupXhrEventHandlers(xhr, options.success, options.error, async (result) => {
103+
// Calculate the size of the result for cache eligibility check
104+
const resultSize = JSON.stringify(result).length;
105+
106+
// Cache the result if it's not bigger than MAX_CACHE_SIZE
107+
const MAX_CACHE_SIZE = 20 * 1024 * 1024;
108+
109+
if(resultSize <= MAX_CACHE_SIZE){
110+
// UPSERT the cache
111+
puter._cache.set(cacheKey, result);
112+
}
113+
114+
resolveRequest(result);
115+
}, rejectRequest);
116+
117+
let dataToSend = {};
118+
if (options.uid !== undefined) {
119+
dataToSend.uid = options.uid;
120+
} else if (options.path !== undefined) {
121+
// If dirPath is not provided or it's not starting with a slash, it means it's a relative path
122+
// in that case, we need to prepend the app's root directory to it
123+
dataToSend.path = getAbsolutePathForApp(options.path);
67124
}
68125

126+
dataToSend.return_subdomains = options.returnSubdomains;
127+
dataToSend.return_permissions = options.returnPermissions;
128+
dataToSend.return_versions = options.returnVersions;
129+
dataToSend.return_size = options.returnSize;
130+
dataToSend.auth_token = this.authToken;
131+
132+
xhr.send(JSON.stringify(dataToSend));
133+
});
134+
135+
// Store the promise and timestamp in the in-flight tracker
136+
inflightRequests.set(deduplicationKey, {
137+
promise: requestPromise,
138+
timestamp: now,
139+
});
140+
141+
// Wait for the request to complete and clean up
142+
try {
143+
const result = await requestPromise;
144+
inflightRequests.delete(deduplicationKey);
69145
resolve(result);
70-
}, reject);
71-
72-
let dataToSend = {};
73-
if (options.uid !== undefined) {
74-
dataToSend.uid = options.uid;
75-
} else if (options.path !== undefined) {
76-
// If dirPath is not provided or it's not starting with a slash, it means it's a relative path
77-
// in that case, we need to prepend the app's root directory to it
78-
dataToSend.path = getAbsolutePathForApp(options.path);
146+
} catch (error) {
147+
inflightRequests.delete(deduplicationKey);
148+
reject(error);
79149
}
80-
81-
dataToSend.return_subdomains = options.returnSubdomains;
82-
dataToSend.return_permissions = options.returnPermissions;
83-
dataToSend.return_versions = options.returnVersions;
84-
dataToSend.return_size = options.returnSize;
85-
dataToSend.auth_token = this.authToken;
86-
87-
xhr.send(JSON.stringify(dataToSend));
88150
})
89151
}
90152

0 commit comments

Comments
 (0)