@heyputer/puter.js 2.0.15 → 2.1.1
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/dist/puter.cjs +2 -2
- package/package.json +2 -2
- package/src/index.js +30 -5
- package/src/modules/Auth.js +127 -42
- package/src/modules/FileSystem/index.js +6 -2
- package/src/modules/FileSystem/operations/stat.js +101 -39
- package/src/modules/UI.js +6 -0
- package/src/modules/Workers.js +51 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyputer/puter.js",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Puter.js - A JavaScript library for interacting with Puter services.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -40,6 +40,6 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@heyputer/kv.js": "^0.2.1",
|
|
43
|
-
"@heyputer/putility": "^1.
|
|
43
|
+
"@heyputer/putility": "^1.1.1"
|
|
44
44
|
}
|
|
45
45
|
}
|
package/src/index.js
CHANGED
|
@@ -506,7 +506,7 @@ const puterInit = (function() {
|
|
|
506
506
|
this.request_rao_();
|
|
507
507
|
|
|
508
508
|
// perform whoami and cache results
|
|
509
|
-
|
|
509
|
+
this.getUser().then((user) => {
|
|
510
510
|
this.whoami = user;
|
|
511
511
|
});
|
|
512
512
|
};
|
|
@@ -722,25 +722,50 @@ const puterInit = (function() {
|
|
|
722
722
|
let documents_path = `/${username}/Documents`;
|
|
723
723
|
let public_path = `/${username}/Public`;
|
|
724
724
|
|
|
725
|
-
// Home
|
|
725
|
+
// item:Home
|
|
726
|
+
if(!puter._cache.get('item:' + home_path)){
|
|
727
|
+
console.log(`/${username} item is not cached, refetching cache`);
|
|
728
|
+
// fetch home
|
|
729
|
+
puter.fs.stat(home_path);
|
|
730
|
+
}
|
|
731
|
+
// item:Desktop
|
|
732
|
+
if(!puter._cache.get('item:' + desktop_path)){
|
|
733
|
+
console.log(`/${username}/Desktop item is not cached, refetching cache`);
|
|
734
|
+
// fetch desktop
|
|
735
|
+
puter.fs.stat(desktop_path);
|
|
736
|
+
}
|
|
737
|
+
// item:Documents
|
|
738
|
+
if(!puter._cache.get('item:' + documents_path)){
|
|
739
|
+
console.log(`/${username}/Documents item is not cached, refetching cache`);
|
|
740
|
+
// fetch documents
|
|
741
|
+
puter.fs.stat(documents_path);
|
|
742
|
+
}
|
|
743
|
+
// item:Public
|
|
744
|
+
if(!puter._cache.get('item:' + public_path)){
|
|
745
|
+
console.log(`/${username}/Public item is not cached, refetching cache`);
|
|
746
|
+
// fetch public
|
|
747
|
+
puter.fs.stat(public_path);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// readdir:Home
|
|
726
751
|
if(!puter._cache.get('readdir:' + home_path)){
|
|
727
752
|
console.log(`/${username} is not cached, refetching cache`);
|
|
728
753
|
// fetch home
|
|
729
754
|
puter.fs.readdir(home_path);
|
|
730
755
|
}
|
|
731
|
-
// Desktop
|
|
756
|
+
// readdir:Desktop
|
|
732
757
|
if(!puter._cache.get('readdir:' + desktop_path)){
|
|
733
758
|
console.log(`/${username}/Desktop is not cached, refetching cache`);
|
|
734
759
|
// fetch desktop
|
|
735
760
|
puter.fs.readdir(desktop_path);
|
|
736
761
|
}
|
|
737
|
-
// Documents
|
|
762
|
+
// readdir:Documents
|
|
738
763
|
if(!puter._cache.get('readdir:' + documents_path)){
|
|
739
764
|
console.log(`/${username}/Documents is not cached, refetching cache`);
|
|
740
765
|
// fetch documents
|
|
741
766
|
puter.fs.readdir(documents_path);
|
|
742
767
|
}
|
|
743
|
-
// Public
|
|
768
|
+
// readdir:Public
|
|
744
769
|
if(!puter._cache.get('readdir:' + public_path)){
|
|
745
770
|
console.log(`/${username}/Public is not cached, refetching cache`);
|
|
746
771
|
// fetch public
|
package/src/modules/Auth.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import * as utils from '../lib/utils.js'
|
|
1
|
+
import * as utils from '../lib/utils.js';
|
|
2
2
|
|
|
3
3
|
class Auth{
|
|
4
4
|
// Used to generate a unique message id for each message sent to the host environment
|
|
5
5
|
// we start from 1 because 0 is falsy and we want to avoid that for the message id
|
|
6
6
|
#messageID = 1;
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
/**
|
|
10
9
|
* Creates a new instance with the given authentication token, API origin, and app ID,
|
|
11
10
|
*
|
|
@@ -14,7 +13,7 @@ class Auth{
|
|
|
14
13
|
* @param {string} APIOrigin - Origin of the API server. Used to build the API endpoint URLs.
|
|
15
14
|
* @param {string} appID - ID of the app to use.
|
|
16
15
|
*/
|
|
17
|
-
constructor
|
|
16
|
+
constructor(context) {
|
|
18
17
|
this.authToken = context.authToken;
|
|
19
18
|
this.APIOrigin = context.APIOrigin;
|
|
20
19
|
this.appID = context.appID;
|
|
@@ -27,22 +26,22 @@ class Auth{
|
|
|
27
26
|
* @memberof [Auth]
|
|
28
27
|
* @returns {void}
|
|
29
28
|
*/
|
|
30
|
-
setAuthToken
|
|
29
|
+
setAuthToken(authToken) {
|
|
31
30
|
this.authToken = authToken;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
/**
|
|
35
34
|
* Sets the API origin.
|
|
36
|
-
*
|
|
35
|
+
*
|
|
37
36
|
* @param {string} APIOrigin - The new API origin.
|
|
38
37
|
* @memberof [Auth]
|
|
39
38
|
* @returns {void}
|
|
40
39
|
*/
|
|
41
|
-
setAPIOrigin
|
|
40
|
+
setAPIOrigin(APIOrigin) {
|
|
42
41
|
this.APIOrigin = APIOrigin;
|
|
43
42
|
}
|
|
44
|
-
|
|
45
|
-
signIn = (options) =>{
|
|
43
|
+
|
|
44
|
+
signIn = (options) => {
|
|
46
45
|
options = options || {};
|
|
47
46
|
|
|
48
47
|
return new Promise((resolve, reject) => {
|
|
@@ -50,17 +49,17 @@ class Auth{
|
|
|
50
49
|
let w = 600;
|
|
51
50
|
let h = 600;
|
|
52
51
|
let title = 'Puter';
|
|
53
|
-
var left = (screen.width/2)-(w/2);
|
|
54
|
-
var top = (screen.height/2)-(h/2);
|
|
55
|
-
|
|
52
|
+
var left = (screen.width / 2) - (w / 2);
|
|
53
|
+
var top = (screen.height / 2) - (h / 2);
|
|
54
|
+
|
|
56
55
|
// Store reference to the popup window
|
|
57
|
-
const popup = window.open(puter.defaultGUIOrigin
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
const popup = window.open(`${puter.defaultGUIOrigin}/action/sign-in?embedded_in_popup=true&msg_id=${msg_id}${window.crossOriginIsolated ? '&cross_origin_isolated=true' : ''}${options.attempt_temp_user_creation ? '&attempt_temp_user_creation=true' : ''}`,
|
|
57
|
+
title,
|
|
58
|
+
`toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=${w}, height=${h}, top=${top}, left=${left}`);
|
|
60
59
|
|
|
61
60
|
// Set up interval to check if popup was closed
|
|
62
61
|
const checkClosed = setInterval(() => {
|
|
63
|
-
if (popup.closed) {
|
|
62
|
+
if ( popup.closed ) {
|
|
64
63
|
clearInterval(checkClosed);
|
|
65
64
|
// Remove the message listener
|
|
66
65
|
window.removeEventListener('message', messageHandler);
|
|
@@ -69,21 +68,23 @@ class Auth{
|
|
|
69
68
|
}, 100);
|
|
70
69
|
|
|
71
70
|
function messageHandler(e) {
|
|
72
|
-
if(e.data.msg_id == msg_id){
|
|
71
|
+
if ( e.data.msg_id == msg_id ){
|
|
73
72
|
// Clear the interval since we got a response
|
|
74
73
|
clearInterval(checkClosed);
|
|
75
|
-
|
|
74
|
+
|
|
76
75
|
// remove redundant attributes
|
|
77
76
|
delete e.data.msg_id;
|
|
78
77
|
delete e.data.msg;
|
|
79
78
|
|
|
80
|
-
if(e.data.success){
|
|
79
|
+
if ( e.data.success ){
|
|
81
80
|
// set the auth token
|
|
82
81
|
puter.setAuthToken(e.data.token);
|
|
83
82
|
|
|
84
83
|
resolve(e.data);
|
|
85
|
-
}else
|
|
84
|
+
} else
|
|
85
|
+
{
|
|
86
86
|
reject(e.data);
|
|
87
|
+
}
|
|
87
88
|
|
|
88
89
|
// delete the listener
|
|
89
90
|
window.removeEventListener('message', messageHandler);
|
|
@@ -92,20 +93,24 @@ class Auth{
|
|
|
92
93
|
|
|
93
94
|
window.addEventListener('message', messageHandler);
|
|
94
95
|
});
|
|
95
|
-
}
|
|
96
|
+
};
|
|
96
97
|
|
|
97
|
-
isSignedIn = () =>{
|
|
98
|
-
if(puter.authToken)
|
|
98
|
+
isSignedIn = () => {
|
|
99
|
+
if ( puter.authToken )
|
|
100
|
+
{
|
|
99
101
|
return true;
|
|
102
|
+
}
|
|
100
103
|
else
|
|
104
|
+
{
|
|
101
105
|
return false;
|
|
102
|
-
|
|
106
|
+
}
|
|
107
|
+
};
|
|
103
108
|
|
|
104
109
|
getUser = function(...args){
|
|
105
110
|
let options;
|
|
106
111
|
|
|
107
112
|
// If first argument is an object, it's the options
|
|
108
|
-
if (typeof args[0] === 'object' && args[0] !== null) {
|
|
113
|
+
if ( typeof args[0] === 'object' && args[0] !== null ) {
|
|
109
114
|
options = args[0];
|
|
110
115
|
} else {
|
|
111
116
|
// Otherwise, we assume separate arguments are provided
|
|
@@ -122,45 +127,125 @@ class Auth{
|
|
|
122
127
|
utils.setupXhrEventHandlers(xhr, options.success, options.error, resolve, reject);
|
|
123
128
|
|
|
124
129
|
xhr.send();
|
|
125
|
-
})
|
|
126
|
-
}
|
|
130
|
+
});
|
|
131
|
+
};
|
|
127
132
|
|
|
128
|
-
signOut = () =>{
|
|
133
|
+
signOut = () => {
|
|
129
134
|
puter.resetAuthToken();
|
|
130
|
-
}
|
|
135
|
+
};
|
|
131
136
|
|
|
132
|
-
async whoami
|
|
137
|
+
async whoami() {
|
|
133
138
|
try {
|
|
134
|
-
const resp = await fetch(this.APIOrigin
|
|
139
|
+
const resp = await fetch(`${this.APIOrigin}/whoami`, {
|
|
135
140
|
headers: {
|
|
136
|
-
Authorization: `Bearer ${this.authToken}
|
|
137
|
-
}
|
|
141
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
142
|
+
},
|
|
138
143
|
});
|
|
139
|
-
|
|
144
|
+
|
|
140
145
|
const result = await resp.json();
|
|
141
|
-
|
|
146
|
+
|
|
142
147
|
// Log the response
|
|
143
|
-
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
148
|
+
if ( globalThis.puter?.apiCallLogger?.isEnabled() ) {
|
|
144
149
|
globalThis.puter.apiCallLogger.logRequest({
|
|
145
150
|
service: 'auth',
|
|
146
151
|
operation: 'whoami',
|
|
147
152
|
params: {},
|
|
148
|
-
result: result
|
|
153
|
+
result: result,
|
|
149
154
|
});
|
|
150
155
|
}
|
|
151
|
-
|
|
156
|
+
|
|
152
157
|
return result;
|
|
153
|
-
} catch
|
|
158
|
+
} catch( error ) {
|
|
154
159
|
// Log the error
|
|
155
|
-
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
160
|
+
if ( globalThis.puter?.apiCallLogger?.isEnabled() ) {
|
|
156
161
|
globalThis.puter.apiCallLogger.logRequest({
|
|
157
162
|
service: 'auth',
|
|
158
163
|
operation: 'whoami',
|
|
159
164
|
params: {},
|
|
160
165
|
error: {
|
|
161
166
|
message: error.message || error.toString(),
|
|
162
|
-
stack: error.stack
|
|
163
|
-
}
|
|
167
|
+
stack: error.stack,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async getMonthlyUsage() {
|
|
176
|
+
try {
|
|
177
|
+
const resp = await fetch(`${this.APIOrigin}/metering/usage`, {
|
|
178
|
+
headers: {
|
|
179
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const result = await resp.json();
|
|
184
|
+
|
|
185
|
+
// Log the response
|
|
186
|
+
if ( globalThis.puter?.apiCallLogger?.isEnabled() ) {
|
|
187
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
188
|
+
service: 'auth',
|
|
189
|
+
operation: 'usage',
|
|
190
|
+
params: {},
|
|
191
|
+
result: result,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return result;
|
|
196
|
+
} catch( error ) {
|
|
197
|
+
// Log the error
|
|
198
|
+
if ( globalThis.puter?.apiCallLogger?.isEnabled() ) {
|
|
199
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
200
|
+
service: 'auth',
|
|
201
|
+
operation: 'usage',
|
|
202
|
+
params: {},
|
|
203
|
+
error: {
|
|
204
|
+
message: error.message || error.toString(),
|
|
205
|
+
stack: error.stack,
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async getDetailedAppUsage(appId) {
|
|
214
|
+
if ( !appId ) {
|
|
215
|
+
throw new Error('appId is required');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const resp = await fetch(`${this.APIOrigin}/metering/usage/${appId}`, {
|
|
220
|
+
headers: {
|
|
221
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const result = await resp.json();
|
|
226
|
+
|
|
227
|
+
// Log the response
|
|
228
|
+
if ( globalThis.puter?.apiCallLogger?.isEnabled() ) {
|
|
229
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
230
|
+
service: 'auth',
|
|
231
|
+
operation: 'detailed_app_usage',
|
|
232
|
+
params: { appId },
|
|
233
|
+
result: result,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return result;
|
|
238
|
+
} catch( error ) {
|
|
239
|
+
// Log the error
|
|
240
|
+
if ( globalThis.puter?.apiCallLogger?.isEnabled() ) {
|
|
241
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
242
|
+
service: 'auth',
|
|
243
|
+
operation: 'detailed_app_usage',
|
|
244
|
+
params: { appId },
|
|
245
|
+
error: {
|
|
246
|
+
message: error.message || error.toString(),
|
|
247
|
+
stack: error.stack,
|
|
248
|
+
},
|
|
164
249
|
});
|
|
165
250
|
}
|
|
166
251
|
throw error;
|
|
@@ -168,4 +253,4 @@ class Auth{
|
|
|
168
253
|
}
|
|
169
254
|
}
|
|
170
255
|
|
|
171
|
-
export default Auth
|
|
256
|
+
export default Auth;
|
|
@@ -134,8 +134,12 @@ export class PuterJSFileSystemModule extends AdvancedBase {
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
this.socket.on('item.added', (item) => {
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
// remove readdir cache for parent
|
|
138
|
+
puter._cache.del('readdir:' + path.dirname(item.path));
|
|
139
|
+
console.log('deleted cache for readdir:' + path.dirname(item.path));
|
|
140
|
+
// remove item cache for parent directory
|
|
141
|
+
puter._cache.del('item:' + path.dirname(item.path));
|
|
142
|
+
console.log('deleted cache for item:' + path.dirname(item.path));
|
|
139
143
|
});
|
|
140
144
|
|
|
141
145
|
this.socket.on('item.updated', (item) => {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import * as utils from '../../../lib/utils.js';
|
|
2
2
|
import getAbsolutePathForApp from '../utils/getAbsolutePathForApp.js';
|
|
3
3
|
|
|
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
|
+
|
|
4
12
|
const stat = async function (...args) {
|
|
5
13
|
let options;
|
|
6
14
|
|
|
@@ -24,17 +32,6 @@ const stat = async function (...args) {
|
|
|
24
32
|
options.consistency = 'strong';
|
|
25
33
|
}
|
|
26
34
|
|
|
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
|
-
|
|
38
35
|
// Generate cache key based on path or uid
|
|
39
36
|
let cacheKey;
|
|
40
37
|
if(options.path){
|
|
@@ -50,41 +47,106 @@ const stat = async function (...args) {
|
|
|
50
47
|
}
|
|
51
48
|
}
|
|
52
49
|
|
|
53
|
-
//
|
|
54
|
-
const
|
|
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
|
+
});
|
|
55
60
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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;
|
|
60
67
|
|
|
61
|
-
//
|
|
62
|
-
|
|
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
|
+
}
|
|
63
83
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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);
|
|
67
124
|
}
|
|
68
125
|
|
|
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);
|
|
69
145
|
resolve(result);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
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);
|
|
79
149
|
}
|
|
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));
|
|
88
150
|
})
|
|
89
151
|
}
|
|
90
152
|
|
package/src/modules/UI.js
CHANGED
|
@@ -765,6 +765,12 @@ class UI extends EventListener {
|
|
|
765
765
|
})
|
|
766
766
|
}
|
|
767
767
|
|
|
768
|
+
requestUpgrade = function() {
|
|
769
|
+
return new Promise((resolve) => {
|
|
770
|
+
this.#postMessageWithCallback('requestUpgrade', resolve, { });
|
|
771
|
+
})
|
|
772
|
+
}
|
|
773
|
+
|
|
768
774
|
showSaveFilePicker = function(content, suggestedName, type){
|
|
769
775
|
const undefinedOnCancel = new putility.libs.promise.TeePromise();
|
|
770
776
|
const resolveOnlyPromise = new Promise((resolve, reject) => {
|
package/src/modules/Workers.js
CHANGED
|
@@ -97,7 +97,7 @@ export class WorkersHandler {
|
|
|
97
97
|
|
|
98
98
|
workerName = workerName.toLocaleLowerCase(); // just incase
|
|
99
99
|
// const driverCall = await puter.drivers.call("workers", "worker-service", "destroy", { authorization: puter.authToken, workerName });
|
|
100
|
-
const driverResult = await utils.make_driver_method(['authorization', 'workerName'], 'workers', "worker-service", 'destroy')(puter.authToken, workerName)
|
|
100
|
+
const driverResult = await utils.make_driver_method(['authorization', 'workerName'], 'workers', "worker-service", 'destroy')(puter.authToken, workerName);
|
|
101
101
|
|
|
102
102
|
if (!driverResult.result) {
|
|
103
103
|
if (!driverResult.result) {
|
|
@@ -116,5 +116,55 @@ export class WorkersHandler {
|
|
|
116
116
|
return true;
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
+
|
|
120
|
+
async getLoggingHandle (workerName) {
|
|
121
|
+
const loggingEndpoint = await utils.make_driver_method([], 'workers', "worker-service", 'getLoggingUrl')(puter.authToken, workerName);
|
|
122
|
+
const socket = new WebSocket(`${loggingEndpoint}/${puter.authToken}/${workerName}`);
|
|
123
|
+
const logStreamObject = new EventTarget();
|
|
124
|
+
logStreamObject.onLog = (data) => { };
|
|
125
|
+
|
|
126
|
+
// Coercibility to ReadableStream
|
|
127
|
+
Object.defineProperty(logStreamObject, 'start', {
|
|
128
|
+
enumerable: false,
|
|
129
|
+
value: async (controller) => {
|
|
130
|
+
socket.addEventListener("message", (event) => {
|
|
131
|
+
controller.enqueue(JSON.parse(event.data));
|
|
132
|
+
});
|
|
133
|
+
socket.addEventListener("close", (event) => {
|
|
134
|
+
try {
|
|
135
|
+
controller.close();
|
|
136
|
+
} catch (e) { }
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
Object.defineProperty(logStreamObject, 'cancel', {
|
|
141
|
+
enumerable: false,
|
|
142
|
+
value: async () => {
|
|
143
|
+
socket.close();
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
socket.addEventListener("message", (event) => {
|
|
149
|
+
const logEvent = new MessageEvent("log", { data: JSON.parse(event.data) });
|
|
150
|
+
|
|
151
|
+
logStreamObject.dispatchEvent(logEvent)
|
|
152
|
+
logStreamObject.onLog(logEvent);
|
|
153
|
+
});
|
|
154
|
+
logStreamObject.close = socket.close;
|
|
155
|
+
return new Promise((res, rej) => {
|
|
156
|
+
let done = false;
|
|
157
|
+
socket.onopen = ()=>{
|
|
158
|
+
done = true;
|
|
159
|
+
res(logStreamObject);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
socket.onerror = () => {
|
|
163
|
+
if (!done) {
|
|
164
|
+
rej("Failed to open logging connection");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
}
|
|
119
169
|
|
|
120
170
|
}
|