@heyputer/puter.js 2.0.13 → 2.0.15

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.
@@ -39,8 +39,6 @@ const stat = async function (...args) {
39
39
  let cacheKey;
40
40
  if(options.path){
41
41
  cacheKey = 'item:' + options.path;
42
- }else if(options.uid){
43
- cacheKey = 'item:' + options.uid;
44
42
  }
45
43
 
46
44
  if(options.consistency === 'eventual' && !options.returnSubdomains && !options.returnPermissions && !options.returnVersions && !options.returnSize){
@@ -62,12 +60,10 @@ const stat = async function (...args) {
62
60
 
63
61
  // Cache the result if it's not bigger than MAX_CACHE_SIZE
64
62
  const MAX_CACHE_SIZE = 20 * 1024 * 1024;
65
- const EXPIRE_TIME = 60 * 60; // 1 hour
66
63
 
67
64
  if(resultSize <= MAX_CACHE_SIZE){
68
65
  // UPSERT the cache
69
- await puter._cache.set('item:' + result.path, result, { EX: EXPIRE_TIME });
70
- await puter._cache.set('item:' + result.uid, result, { EX: EXPIRE_TIME });
66
+ puter._cache.set(cacheKey, result);
71
67
  }
72
68
 
73
69
  resolve(result);
@@ -1,6 +1,6 @@
1
+ import path from "../../../lib/path.js";
1
2
  import * as utils from '../../../lib/utils.js';
2
3
  import getAbsolutePathForApp from '../utils/getAbsolutePathForApp.js';
3
- import path from "../../../lib/path.js"
4
4
 
5
5
  const upload = async function(items, dirPath, options = {}){
6
6
  return new Promise(async (resolve, reject) => {
@@ -429,9 +429,6 @@ const upload = async function(items, dirPath, options = {}){
429
429
  options.start();
430
430
  }
431
431
 
432
- // todo: EXTREMELY NAIVE CACHE PURGE
433
- puter._cache.flushall();
434
-
435
432
  // send request
436
433
  xhr.send(fd);
437
434
  })
@@ -1,4 +1,4 @@
1
- import path from "../../../lib/path.js"
1
+ import path from "../../../lib/path.js";
2
2
  import getAbsolutePathForApp from '../utils/getAbsolutePathForApp.js';
3
3
 
4
4
  const write = async function (targetPath, data, options = {}) {
@@ -55,9 +55,6 @@ const write = async function (targetPath, data, options = {}) {
55
55
  throw new Error({ code: 'field_invalid', message: 'write() data parameter is an invalid type' });
56
56
  }
57
57
 
58
- // todo: EXTREMELY NAIVE CACHE PURGE
59
- puter._cache.flushall();
60
-
61
58
  // perform upload
62
59
  return this.upload(data, parent, options);
63
60
  }
package/src/modules/KV.js CHANGED
@@ -19,7 +19,7 @@ const gui_cache_keys = [
19
19
  ];
20
20
  class KV{
21
21
  MAX_KEY_SIZE = 1024;
22
- MAX_VALUE_SIZE = 400 * 1024;
22
+ MAX_VALUE_SIZE = 399 * 1024;
23
23
 
24
24
  /**
25
25
  * Creates a new instance with the given authentication token, API origin, and app ID,
@@ -50,7 +50,7 @@ class KV{
50
50
  args: {
51
51
  key: gui_cache_keys,
52
52
  },
53
- auth_token: this.authToken
53
+ auth_token: this.authToken,
54
54
  }),
55
55
  });
56
56
  const arr_values = await resp.json();
@@ -95,14 +95,25 @@ class KV{
95
95
  }
96
96
 
97
97
  /**
98
- * Resolves to 'true' on success, or rejects with an error on failure
99
- *
100
- * `key` cannot be undefined or null.
101
- * `key` size cannot be larger than 1mb.
102
- * `value` size cannot be larger than 10mb.
103
- * `expireAt` is a timestamp in sec since epoch. If provided, the key will expire at the given time.
98
+ * @typedef {function(key: string, value: any, expireAt?: number): Promise<boolean>} SetFunction
99
+ * Resolves to 'true' on success, or rejects with an error on failure.
100
+ * @param {string} key - Cannot be undefined or null. Cannot be larger than 1KB.
101
+ * @param {any} value - Cannot be larger than 399KB.
102
+ * @param {number} [expireAt] - Optional expiration time for the key. Note that clients with a clock that is not in sync with the server may experience issues with this method.
103
+ * @memberof KV
104
104
  */
105
+
106
+ /** @type {SetFunction} */
105
107
  set = utils.make_driver_method(['key', 'value', 'expireAt'], 'puter-kvstore', undefined, 'set', {
108
+ /**
109
+ *
110
+ * @param {object} args
111
+ * @param {string} args.key
112
+ * @param {any} args.value
113
+ * @param {number} [args.expireAt]
114
+ * @memberof [KV]
115
+ * @returns
116
+ */
106
117
  preprocess: (args) => {
107
118
  // key cannot be undefined or null
108
119
  if ( args.key === undefined || args.key === null ){
@@ -110,11 +121,11 @@ class KV{
110
121
  }
111
122
  // key size cannot be larger than MAX_KEY_SIZE
112
123
  if ( args.key.length > this.MAX_KEY_SIZE ){
113
- throw { message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' };
124
+ throw { message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' };
114
125
  }
115
126
  // value size cannot be larger than MAX_VALUE_SIZE
116
127
  if ( args.value && args.value.length > this.MAX_VALUE_SIZE ){
117
- throw { message: 'Value size cannot be larger than ' + this.MAX_VALUE_SIZE, code: 'value_too_large' };
128
+ throw { message: `Value size cannot be larger than ${this.MAX_VALUE_SIZE}`, code: 'value_too_large' };
118
129
  }
119
130
  return args;
120
131
  },
@@ -143,7 +154,7 @@ class KV{
143
154
  preprocess: (args) => {
144
155
  // key size cannot be larger than MAX_KEY_SIZE
145
156
  if ( args.key.length > this.MAX_KEY_SIZE ){
146
- throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
157
+ throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' });
147
158
  }
148
159
 
149
160
  return args;
@@ -162,11 +173,11 @@ class KV{
162
173
  }
163
174
 
164
175
  options.key = args[0];
165
- options.amount = args[1] ?? 1;
176
+ options.pathAndAmountMap = !args[1] ? { '': 1 } : typeof args[1] === 'number' ? { '': args[1] } : args[1];
166
177
 
167
178
  // key size cannot be larger than MAX_KEY_SIZE
168
179
  if ( options.key.length > this.MAX_KEY_SIZE ){
169
- throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
180
+ throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' });
170
181
  }
171
182
 
172
183
  return utils.make_driver_method(['key'], 'puter-kvstore', undefined, 'incr').call(this, options);
@@ -181,37 +192,53 @@ class KV{
181
192
  }
182
193
 
183
194
  options.key = args[0];
184
- options.amount = args[1] ?? 1;
195
+ options.pathAndAmountMap = !args[1] ? { '': 1 } : typeof args[1] === 'number' ? { '': args[1] } : args[1];
185
196
 
186
197
  // key size cannot be larger than MAX_KEY_SIZE
187
198
  if ( options.key.length > this.MAX_KEY_SIZE ){
188
- throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
199
+ throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' });
189
200
  }
190
201
 
191
202
  return utils.make_driver_method(['key'], 'puter-kvstore', undefined, 'decr').call(this, options);
192
203
  };
193
204
 
194
- expire = async (...args) => {
205
+ /**
206
+ * Set a time to live (in seconds) on a key. After the time to live has expired, the key will be deleted.
207
+ * Prefer this over expireAt if you want timestamp to be set by the server, to avoid issues with clock drift.
208
+ * @param {string} key - The key to set the expiration on.
209
+ * @param {number} ttl - The ttl
210
+ * @memberof [KV]
211
+ * @returns
212
+ */
213
+ expire = async (key, ttl) => {
195
214
  let options = {};
196
- options.key = args[0];
197
- options.ttl = args[1];
215
+ options.key = key;
216
+ options.ttl = ttl;
198
217
 
199
218
  // key size cannot be larger than MAX_KEY_SIZE
200
219
  if ( options.key.length > this.MAX_KEY_SIZE ){
201
- throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
220
+ throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' });
202
221
  }
203
222
 
204
223
  return utils.make_driver_method(['key', 'ttl'], 'puter-kvstore', undefined, 'expire').call(this, options);
205
224
  };
206
225
 
207
- expireAt = async (...args) => {
226
+ /**
227
+ *
228
+ * Set the expiration for a key as a UNIX timestamp (in seconds). After the time has passed, the key will be deleted.
229
+ * Note that clients with a clock that is not in sync with the server may experience issues with this method.
230
+ * @param {string} key - The key to set the expiration on.
231
+ * @param {number} timestamp - The timestamp (in seconds since epoch) when the key will expire.
232
+ * @memberof [KV]
233
+ * @returns
234
+ */
235
+ expireAt = async (key, timestamp) => {
208
236
  let options = {};
209
- options.key = args[0];
210
- options.timestamp = args[1];
211
-
237
+ options.key = key;
238
+ options.timestamp = timestamp;
212
239
  // key size cannot be larger than MAX_KEY_SIZE
213
240
  if ( options.key.length > this.MAX_KEY_SIZE ){
214
- throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
241
+ throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' });
215
242
  }
216
243
 
217
244
  return utils.make_driver_method(['key', 'timestamp'], 'puter-kvstore', undefined, 'expireAt').call(this, options);
@@ -223,7 +250,7 @@ class KV{
223
250
  preprocess: (args) => {
224
251
  // key size cannot be larger than this.MAX_KEY_SIZE
225
252
  if ( args.key.length > this.MAX_KEY_SIZE ){
226
- throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
253
+ throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' });
227
254
  }
228
255
 
229
256
  return args;
@@ -292,7 +319,7 @@ function globMatch(pattern, str) {
292
319
  .replace(/\\\]/g, ']') // Replace ] with ]
293
320
  .replace(/\\\^/g, '^'); // Replace ^ with ^
294
321
 
295
- let re = new RegExp('^' + regexPattern + '$');
322
+ let re = new RegExp(`^${regexPattern}$`);
296
323
  return re.test(str);
297
324
  }
298
325
 
package/src/modules/UI.js CHANGED
@@ -275,6 +275,7 @@ class UI extends EventListener {
275
275
  // Bind the message event listener to the window
276
276
  let lastDraggedOverElement = null;
277
277
  (globalThis.document) && window.addEventListener('message', async (e) => {
278
+ if (!e.data) return;
278
279
  // `error`
279
280
  if(e.data.error){
280
281
  throw e.data.error;
@@ -395,6 +396,12 @@ class UI extends EventListener {
395
396
  this.#callbackFunctions[e.data.original_msg_id](appDataItem);
396
397
  }
397
398
  }
399
+ // instancesOpenSucceeded
400
+ else if(e.data.msg === 'instancesOpenSucceeded'){
401
+ if(e.data.original_msg_id && this.#callbackFunctions[e.data.original_msg_id]){
402
+ this.#callbackFunctions[e.data.original_msg_id](e.data.instancesOpen);
403
+ }
404
+ }
398
405
  // readAppDataFileSucceeded
399
406
  else if(e.data.msg === 'readAppDataFileSucceeded'){
400
407
  let appDataItem = new FSItem(e.data.item);
@@ -657,6 +664,12 @@ class UI extends EventListener {
657
664
  })
658
665
  }
659
666
 
667
+ instancesOpen = function(callback) {
668
+ return new Promise((resolve) => {
669
+ this.#postMessageWithCallback('getInstancesOpen', resolve, { });
670
+ })
671
+ }
672
+
660
673
  socialShare = function(url, message, options, callback) {
661
674
  return new Promise((resolve) => {
662
675
  this.#postMessageWithCallback('socialShare', resolve, { url, message, options });
@@ -917,6 +930,18 @@ class UI extends EventListener {
917
930
  })
918
931
  }
919
932
 
933
+ showWindow = function() {
934
+ this.#postMessageWithObject('showWindow');
935
+ }
936
+
937
+ hideWindow = function() {
938
+ this.#postMessageWithObject('hideWindow');
939
+ }
940
+
941
+ toggleWindow = function() {
942
+ this.#postMessageWithObject('toggleWindow');
943
+ }
944
+
920
945
  setMenubar = function(spec) {
921
946
  this.#postMessageWithObject('setMenubar', spec);
922
947
  }
@@ -1541,9 +1566,12 @@ class UI extends EventListener {
1541
1566
  * console.log(`Current language: ${currentLang}`); // e.g., "Current language: fr"
1542
1567
  */
1543
1568
  getLanguage() {
1544
- // In GUI environment, access the global locale directly
1569
+ // resolve with the current language code if in GUI environment
1545
1570
  if(this.env === 'gui'){
1546
- return window.locale;
1571
+ // resolve with the current language code
1572
+ return new Promise((resolve) => {
1573
+ resolve(window.locale);
1574
+ });
1547
1575
  }
1548
1576
 
1549
1577
  return new Promise((resolve) => {
@@ -11,6 +11,10 @@ export class PTLSSocket extends PSocket {
11
11
  super(...args);
12
12
  super.on("open", (async() => {
13
13
  if (!rustls) {
14
+ // Safari exists unfortunately without good ReadableStream support. Until that is fixed we need this.
15
+ if (!globalThis.ReadableByteStreamController) {
16
+ await import( /* webpackIgnore: true */ "https://unpkg.com/web-streams-polyfill@3.0.2/dist/polyfill.js");
17
+ }
14
18
  rustls = (await import( /* webpackIgnore: true */ "https://puter-net.b-cdn.net/rustls.js"))
15
19
  await rustls.default("https://puter-net.b-cdn.net/rustls.wasm")
16
20
  }
@@ -20,7 +20,7 @@ export class XDIncomingService extends putility.concepts.Service {
20
20
  }
21
21
 
22
22
  const data = event.data;
23
-
23
+ if ( ! data ) return;
24
24
  const tag = data.$;
25
25
  if ( ! tag ) return;
26
26
  if ( ! this.tagged_listeners_[tag] ) return;