@heyputer/puter.js 2.0.14 → 2.0.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyputer/puter.js",
3
- "version": "2.0.14",
3
+ "version": "2.0.16",
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",
@@ -25,15 +25,17 @@
25
25
  ],
26
26
  "scripts": {
27
27
  "start-server": "npx http-server --cors -c-1",
28
- "start-webpack": "webpack && webpack --output-filename puter.dev.js --watch --devtool source-map",
28
+ "start-webpack": "webpack --stats=errors-only && webpack --output-filename puter.dev.js --watch --devtool source-map --stats=errors-only",
29
29
  "start": "concurrently \"npm run start-server\" \"npm run start-webpack\"",
30
+ "test": "npx http-server --cors -c-1 -o /test",
30
31
  "build": "webpack && { echo \"// Copyright 2024-present Puter Technologies Inc. All rights reserved.\"; echo \"// Generated on $(date '+%Y-%m-%d %H:%M')\n\"; cat ./dist/puter.js; } > temp && mv temp ./dist/puter.js",
31
- "prepublishOnly": "npm run build && mv dist/puter.js dist/puter.cjs && npm version patch && git add package.json && git commit -m \"chore: bump puter-js version and publish \" && git push"
32
+ "prepublishOnly": "npm run build && mv dist/puter.js dist/puter.cjs && npm version patch"
32
33
  },
33
34
  "author": "Puter Technologies Inc.",
34
35
  "license": "Apache-2.0",
35
36
  "devDependencies": {
36
37
  "concurrently": "^8.2.2",
38
+ "http-server": "^14.1.1",
37
39
  "webpack-cli": "^5.1.4"
38
40
  },
39
41
  "dependencies": {
package/src/index.js CHANGED
@@ -131,6 +131,7 @@ const puterInit = (function() {
131
131
 
132
132
  // Initialize the cache using kv.js
133
133
  this._cache = new kvjs({dbName: 'puter_cache'});
134
+ this._opscache = new kvjs();
134
135
 
135
136
  // "modules" in puter.js are external interfaces for the developer
136
137
  this.modules_ = [];
@@ -493,10 +494,21 @@ const puterInit = (function() {
493
494
  console.error('Error accessing localStorage:', error);
494
495
  }
495
496
  }
497
+ // initialize loop for updating caches for major directories
498
+ if(this.env === 'gui'){
499
+ // check and update gui fs cache regularly
500
+ setInterval(puter.checkAndUpdateGUIFScache, 10000);
501
+ }
496
502
  // reinitialize submodules
497
503
  this.updateSubmodules();
504
+
498
505
  // rao
499
506
  this.request_rao_();
507
+
508
+ // perform whoami and cache results
509
+ this.getUser().then((user) => {
510
+ this.whoami = user;
511
+ });
500
512
  };
501
513
 
502
514
  setAPIOrigin = function(APIOrigin) {
@@ -666,7 +678,12 @@ const puterInit = (function() {
666
678
  // If we went from online to offline, purge the cache
667
679
  if ( wasOnline && !isOnline ) {
668
680
  console.log('Network connection lost - purging cache');
669
- this.purgeCache();
681
+ try {
682
+ this._cache.flushall();
683
+ console.log('Cache purged successfully');
684
+ } catch( error ) {
685
+ console.error('Error purging cache:', error);
686
+ }
670
687
  }
671
688
 
672
689
  // Update the previous state
@@ -688,18 +705,73 @@ const puterInit = (function() {
688
705
  };
689
706
 
690
707
  /**
691
- * Purges all cached data
692
- * @public
708
+ * Checks and updates the GUI FS cache for most-commonly used paths
709
+ * @private
693
710
  */
694
- purgeCache = function() {
695
- try {
696
- this._cache.flushall();
697
- console.log('Cache purged successfully');
698
- } catch( error ) {
699
- console.error('Error purging cache:', error);
711
+ checkAndUpdateGUIFScache = function(){
712
+ // only run in gui environment
713
+ if(puter.env !== 'gui') return;
714
+ // only run if user is authenticated
715
+ if(!puter.whoami) return;
716
+
717
+ let username = puter.whoami.username;
718
+
719
+ // common paths
720
+ let home_path = `/${username}`;
721
+ let desktop_path = `/${username}/Desktop`;
722
+ let documents_path = `/${username}/Documents`;
723
+ let public_path = `/${username}/Public`;
724
+
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
751
+ if(!puter._cache.get('readdir:' + home_path)){
752
+ console.log(`/${username} is not cached, refetching cache`);
753
+ // fetch home
754
+ puter.fs.readdir(home_path);
755
+ }
756
+ // readdir:Desktop
757
+ if(!puter._cache.get('readdir:' + desktop_path)){
758
+ console.log(`/${username}/Desktop is not cached, refetching cache`);
759
+ // fetch desktop
760
+ puter.fs.readdir(desktop_path);
761
+ }
762
+ // readdir:Documents
763
+ if(!puter._cache.get('readdir:' + documents_path)){
764
+ console.log(`/${username}/Documents is not cached, refetching cache`);
765
+ // fetch documents
766
+ puter.fs.readdir(documents_path);
767
+ }
768
+ // readdir:Public
769
+ if(!puter._cache.get('readdir:' + public_path)){
770
+ console.log(`/${username}/Public is not cached, refetching cache`);
771
+ // fetch public
772
+ puter.fs.readdir(public_path);
700
773
  }
701
- };
702
-
774
+ }
703
775
  }
704
776
 
705
777
  // Create a new Puter object and return it
package/src/init.cjs CHANGED
@@ -19,7 +19,7 @@ const init = (authToken) => {
19
19
  }
20
20
  });
21
21
  goodContext.globalThis = goodContext;
22
- const code = readFileSync(`${resolve(__filename, '..')}/../dist/puter.js`, 'utf8');
22
+ const code = readFileSync(`${resolve(__filename, '..')}/../dist/puter.cjs`, 'utf8');
23
23
  const context = vm.createContext(goodContext);
24
24
  vm.runInNewContext(code, context);
25
25
  if ( authToken ) {
@@ -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 (context) {
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 (authToken) {
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 (APIOrigin) {
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 + '/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' : ''),
58
- title,
59
- 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width='+w+', height='+h+', top='+top+', left='+left);
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 + '/whoami', {
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 (error) {
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;
@@ -123,62 +123,31 @@ export class PuterJSFileSystemModule extends AdvancedBase {
123
123
  // });
124
124
 
125
125
  this.socket.on('item.renamed', (item) => {
126
- // delete old item from cache
127
- puter._cache.del('item:' + item.old_path);
128
- // if a directory
129
- if(item.is_dir){
130
- // delete readdir
131
- puter._cache.del('readdir:' + item.old_path);
132
- // descendants items
133
- const descendants = puter._cache.keys('item:' + item.old_path + '/*');
134
- for(const descendant of descendants){
135
- console.log('Deleting cache for:', descendant);
136
- puter._cache.del(descendant);
137
- }
138
- // descendants readdirs
139
- const descendants_readdir = puter._cache.keys('readdir:' + item.old_path + '/*');
140
- for(const descendant of descendants_readdir){
141
- console.log('Deleting cache for:', descendant);
142
- puter._cache.del(descendant);
143
- }
144
- }
145
- // parent readdir
146
- puter._cache.del('readdir:' + path.dirname(item.old_path));
126
+ puter._cache.flushall();
127
+ console.log('Flushed cache for item.renamed');
147
128
  });
148
129
 
149
130
  this.socket.on('item.removed', (item) => {
150
131
  // check original_client_socket_id and if it matches this.socket.id, don't invalidate cache
151
132
  puter._cache.flushall();
152
- console.log('Flushed cache for item.deleted');
133
+ console.log('Flushed cache for item.removed');
153
134
  });
154
135
 
155
136
  this.socket.on('item.added', (item) => {
156
- // delete item from cache
157
- puter._cache.del('item:' + item.path);
158
- // delete readdir from cache
159
- puter._cache.del('readdir:' + item.path);
160
- // delete descendant items from cache
161
- const descendant_items = puter._cache.keys('item:' + item.path + '/*');
162
- for(const descendant of descendant_items){
163
- puter._cache.del(descendant);
164
- }
165
- // delete descendant readdirs from cache
166
- const descendant_readdirs = puter._cache.keys('readdir:' + item.path + '/*');
167
- for(const descendant of descendant_readdirs){
168
- puter._cache.del(descendant);
169
- }
170
- // delete parent readdir from cache
137
+ // remove readdir cache for parent
171
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));
172
143
  });
173
144
 
174
145
  this.socket.on('item.updated', (item) => {
175
- // check original_client_socket_id and if it matches this.socket.id, don't invalidate cache
176
146
  puter._cache.flushall();
177
147
  console.log('Flushed cache for item.updated');
178
148
  });
179
149
 
180
150
  this.socket.on('item.moved', (item) => {
181
- // check original_client_socket_id and if it matches this.socket.id, don't invalidate cache
182
151
  puter._cache.flushall();
183
152
  console.log('Flushed cache for item.moved');
184
153
  });