@heyputer/puter.js 1.0.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.
Files changed (74) hide show
  1. package/APACHE_LICENSE.txt +201 -0
  2. package/README.md +88 -0
  3. package/doc/devlog.md +49 -0
  4. package/package.json +31 -0
  5. package/src/bg.png +0 -0
  6. package/src/bg.webp +0 -0
  7. package/src/index.js +745 -0
  8. package/src/lib/APICallLogger.js +110 -0
  9. package/src/lib/EventListener.js +51 -0
  10. package/src/lib/RequestError.js +6 -0
  11. package/src/lib/filesystem/APIFS.js +73 -0
  12. package/src/lib/filesystem/CacheFS.js +243 -0
  13. package/src/lib/filesystem/PostMessageFS.js +40 -0
  14. package/src/lib/filesystem/definitions.js +39 -0
  15. package/src/lib/path.js +509 -0
  16. package/src/lib/polyfills/localStorage.js +92 -0
  17. package/src/lib/polyfills/xhrshim.js +233 -0
  18. package/src/lib/socket.io/socket.io.esm.min.js +7 -0
  19. package/src/lib/socket.io/socket.io.esm.min.js.map +1 -0
  20. package/src/lib/socket.io/socket.io.js +4385 -0
  21. package/src/lib/socket.io/socket.io.js.map +1 -0
  22. package/src/lib/socket.io/socket.io.min.js +7 -0
  23. package/src/lib/socket.io/socket.io.min.js.map +1 -0
  24. package/src/lib/socket.io/socket.io.msgpack.min.js +7 -0
  25. package/src/lib/socket.io/socket.io.msgpack.min.js.map +1 -0
  26. package/src/lib/utils.js +620 -0
  27. package/src/lib/xdrpc.js +104 -0
  28. package/src/modules/AI.js +680 -0
  29. package/src/modules/Apps.js +215 -0
  30. package/src/modules/Auth.js +171 -0
  31. package/src/modules/Debug.js +39 -0
  32. package/src/modules/Drivers.js +278 -0
  33. package/src/modules/FSItem.js +139 -0
  34. package/src/modules/FileSystem/index.js +187 -0
  35. package/src/modules/FileSystem/operations/copy.js +64 -0
  36. package/src/modules/FileSystem/operations/deleteFSEntry.js +59 -0
  37. package/src/modules/FileSystem/operations/getReadUrl.js +42 -0
  38. package/src/modules/FileSystem/operations/mkdir.js +62 -0
  39. package/src/modules/FileSystem/operations/move.js +75 -0
  40. package/src/modules/FileSystem/operations/read.js +46 -0
  41. package/src/modules/FileSystem/operations/readdir.js +102 -0
  42. package/src/modules/FileSystem/operations/rename.js +58 -0
  43. package/src/modules/FileSystem/operations/sign.js +103 -0
  44. package/src/modules/FileSystem/operations/space.js +40 -0
  45. package/src/modules/FileSystem/operations/stat.js +95 -0
  46. package/src/modules/FileSystem/operations/symlink.js +55 -0
  47. package/src/modules/FileSystem/operations/upload.js +440 -0
  48. package/src/modules/FileSystem/operations/write.js +65 -0
  49. package/src/modules/FileSystem/utils/getAbsolutePathForApp.js +21 -0
  50. package/src/modules/Hosting.js +138 -0
  51. package/src/modules/KV.js +301 -0
  52. package/src/modules/OS.js +95 -0
  53. package/src/modules/Perms.js +109 -0
  54. package/src/modules/PuterDialog.js +481 -0
  55. package/src/modules/Threads.js +75 -0
  56. package/src/modules/UI.js +1555 -0
  57. package/src/modules/Util.js +38 -0
  58. package/src/modules/Workers.js +120 -0
  59. package/src/modules/networking/PSocket.js +87 -0
  60. package/src/modules/networking/PTLS.js +100 -0
  61. package/src/modules/networking/PWispHandler.js +89 -0
  62. package/src/modules/networking/parsers.js +157 -0
  63. package/src/modules/networking/requests.js +282 -0
  64. package/src/services/APIAccess.js +46 -0
  65. package/src/services/FSRelay.js +20 -0
  66. package/src/services/Filesystem.js +122 -0
  67. package/src/services/NoPuterYet.js +20 -0
  68. package/src/services/XDIncoming.js +44 -0
  69. package/test/ai.test.js +214 -0
  70. package/test/fs.test.js +798 -0
  71. package/test/index.html +1183 -0
  72. package/test/kv.test.js +548 -0
  73. package/test/txt2speech.test.js +178 -0
  74. package/webpack.config.js +25 -0
@@ -0,0 +1,301 @@
1
+ import { TeePromise } from '@heyputer/putility/src/libs/promise.js';
2
+ import * as utils from '../lib/utils.js';
3
+
4
+ const gui_cache_keys = [
5
+ 'has_set_default_app_user_permissions',
6
+ 'window_sidebar_width',
7
+ 'sidebar_items',
8
+ 'menubar_style',
9
+ 'user_preferences.auto_arrange_desktop',
10
+ 'user_preferences.show_hidden_files',
11
+ 'user_preferences.language',
12
+ 'user_preferences.clock_visible',
13
+ 'toolbar_auto_hide_enabled',
14
+ 'has_seen_welcome_window',
15
+ 'desktop_item_positions',
16
+ 'desktop_icons_hidden',
17
+ 'taskbar_position',
18
+ 'has_seen_toolbar_animation',
19
+ ];
20
+ class KV{
21
+ MAX_KEY_SIZE = 1024;
22
+ MAX_VALUE_SIZE = 400 * 1024;
23
+
24
+ /**
25
+ * Creates a new instance with the given authentication token, API origin, and app ID,
26
+ *
27
+ * @class
28
+ * @param {string} authToken - Token used to authenticate the user.
29
+ * @param {string} APIOrigin - Origin of the API server. Used to build the API endpoint URLs.
30
+ * @param {string} appID - ID of the app to use.
31
+ */
32
+ constructor(context) {
33
+ this.authToken = context.authToken;
34
+ this.APIOrigin = context.APIOrigin;
35
+ this.appID = context.appID;
36
+
37
+ this.gui_cached = new TeePromise();
38
+ this.gui_cache_init = new TeePromise();
39
+ (async () => {
40
+ await this.gui_cache_init;
41
+ this.gui_cache_init = null;
42
+ const resp = await fetch(`${this.APIOrigin}/drivers/call`, {
43
+ method: 'POST',
44
+ headers: {
45
+ 'Content-Type': 'text/plain;actually=json',
46
+ },
47
+ body: JSON.stringify({
48
+ interface: 'puter-kvstore',
49
+ method: 'get',
50
+ args: {
51
+ key: gui_cache_keys,
52
+ },
53
+ auth_token: this.authToken
54
+ }),
55
+ });
56
+ const arr_values = await resp.json();
57
+ if ( ! Array.isArray(arr_values?.result) ) {
58
+ this.gui_cached.resolve({});
59
+ setTimeout(() => {
60
+ this.gui_cached = null;
61
+ }, 4000);
62
+ return;
63
+ }
64
+ const obj = {};
65
+ for ( let i = 0; i < gui_cache_keys.length; i++ ) {
66
+ obj[gui_cache_keys[i]] = arr_values.result[i];
67
+ }
68
+ this.gui_cached.resolve(obj);
69
+ setTimeout(() => {
70
+ this.gui_cached = null;
71
+ }, 4000);
72
+ })();
73
+ }
74
+
75
+ /**
76
+ * Sets a new authentication token.
77
+ *
78
+ * @param {string} authToken - The new authentication token.
79
+ * @memberof [KV]
80
+ * @returns {void}
81
+ */
82
+ setAuthToken(authToken) {
83
+ this.authToken = authToken;
84
+ }
85
+
86
+ /**
87
+ * Sets the API origin.
88
+ *
89
+ * @param {string} APIOrigin - The new API origin.
90
+ * @memberof [KV]
91
+ * @returns {void}
92
+ */
93
+ setAPIOrigin(APIOrigin) {
94
+ this.APIOrigin = APIOrigin;
95
+ }
96
+
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.
104
+ */
105
+ set = utils.make_driver_method(['key', 'value', 'expireAt'], 'puter-kvstore', undefined, 'set', {
106
+ preprocess: (args) => {
107
+ console.log(args);
108
+
109
+ // key cannot be undefined or null
110
+ if ( args.key === undefined || args.key === null ){
111
+ throw { message: 'Key cannot be undefined', code: 'key_undefined' };
112
+ }
113
+ // key size cannot be larger than MAX_KEY_SIZE
114
+ if ( args.key.length > this.MAX_KEY_SIZE ){
115
+ throw { message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' };
116
+ }
117
+ // value size cannot be larger than MAX_VALUE_SIZE
118
+ if ( args.value && args.value.length > this.MAX_VALUE_SIZE ){
119
+ throw { message: 'Value size cannot be larger than ' + this.MAX_VALUE_SIZE, code: 'value_too_large' };
120
+ }
121
+ return args;
122
+ },
123
+ });
124
+
125
+ /**
126
+ * Resolves to the value if the key exists, or `undefined` if the key does not exist. Rejects with an error on failure.
127
+ */
128
+ async get(...args) {
129
+ // Condition for gui boot cache
130
+ if (
131
+ typeof args[0] === 'string' &&
132
+ gui_cache_keys.includes(args[0]) &&
133
+ this.gui_cached !== null
134
+ ) {
135
+ this.gui_cache_init && this.gui_cache_init.resolve();
136
+ const cache = await this.gui_cached;
137
+ return cache[args[0]];
138
+ }
139
+
140
+ // Normal get
141
+ return await this.get_(...args);
142
+ }
143
+
144
+ get_ = utils.make_driver_method(['key'], 'puter-kvstore', undefined, 'get', {
145
+ preprocess: (args) => {
146
+ // key size cannot be larger than MAX_KEY_SIZE
147
+ if ( args.key.length > this.MAX_KEY_SIZE ){
148
+ throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
149
+ }
150
+
151
+ return args;
152
+ },
153
+ transform: (res) => {
154
+ return res;
155
+ },
156
+ });
157
+
158
+ incr = async (...args) => {
159
+ let options = {};
160
+
161
+ // arguments are required
162
+ if ( !args || args.length === 0 ){
163
+ throw ({ message: 'Arguments are required', code: 'arguments_required' });
164
+ }
165
+
166
+ options.key = args[0];
167
+ options.amount = args[1] ?? 1;
168
+
169
+ // key size cannot be larger than MAX_KEY_SIZE
170
+ if ( options.key.length > this.MAX_KEY_SIZE ){
171
+ throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
172
+ }
173
+
174
+ return utils.make_driver_method(['key'], 'puter-kvstore', undefined, 'incr').call(this, options);
175
+ };
176
+
177
+ decr = async (...args) => {
178
+ let options = {};
179
+
180
+ // arguments are required
181
+ if ( !args || args.length === 0 ){
182
+ throw ({ message: 'Arguments are required', code: 'arguments_required' });
183
+ }
184
+
185
+ options.key = args[0];
186
+ options.amount = args[1] ?? 1;
187
+
188
+ // key size cannot be larger than MAX_KEY_SIZE
189
+ if ( options.key.length > this.MAX_KEY_SIZE ){
190
+ throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
191
+ }
192
+
193
+ return utils.make_driver_method(['key'], 'puter-kvstore', undefined, 'decr').call(this, options);
194
+ };
195
+
196
+ expire = async (...args) => {
197
+ let options = {};
198
+ options.key = args[0];
199
+ options.ttl = args[1];
200
+
201
+ // key size cannot be larger than MAX_KEY_SIZE
202
+ if ( options.key.length > this.MAX_KEY_SIZE ){
203
+ throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
204
+ }
205
+
206
+ return utils.make_driver_method(['key', 'ttl'], 'puter-kvstore', undefined, 'expire').call(this, options);
207
+ };
208
+
209
+ expireAt = async (...args) => {
210
+ let options = {};
211
+ options.key = args[0];
212
+ options.timestamp = args[1];
213
+
214
+ // key size cannot be larger than MAX_KEY_SIZE
215
+ if ( options.key.length > this.MAX_KEY_SIZE ){
216
+ throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
217
+ }
218
+
219
+ return utils.make_driver_method(['key', 'timestamp'], 'puter-kvstore', undefined, 'expireAt').call(this, options);
220
+ };
221
+
222
+ // resolves to 'true' on success, or rejects with an error on failure
223
+ // will still resolve to 'true' if the key does not exist
224
+ del = utils.make_driver_method(['key'], 'puter-kvstore', undefined, 'del', {
225
+ preprocess: (args) => {
226
+ // key size cannot be larger than this.MAX_KEY_SIZE
227
+ if ( args.key.length > this.MAX_KEY_SIZE ){
228
+ throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' });
229
+ }
230
+
231
+ return args;
232
+ },
233
+ });
234
+
235
+ list = async (...args) => {
236
+ let options = {};
237
+ let pattern;
238
+ let returnValues = false;
239
+
240
+ // list(true) or list(pattern, true) will return the key-value pairs
241
+ if ( (args && args.length === 1 && args[0] === true) || (args && args.length === 2 && args[1] === true) ){
242
+ options = {};
243
+ returnValues = true;
244
+ }
245
+ // return only the keys, default behavior
246
+ else {
247
+ options = { as: 'keys' };
248
+ }
249
+
250
+ // list(pattern)
251
+ // list(pattern, true)
252
+ if ( (args && args.length === 1 && typeof args[0] === 'string') || (args && args.length === 2 && typeof args[0] === 'string' && args[1] === true) ){
253
+ pattern = args[0];
254
+ }
255
+
256
+ return utils.make_driver_method([], 'puter-kvstore', undefined, 'list', {
257
+ transform: (res) => {
258
+ // glob pattern was provided
259
+ if ( pattern ){
260
+ // consider both the key and the value
261
+ if ( !returnValues ) {
262
+ let keys = res.filter((key) => {
263
+ return globMatch(pattern, key);
264
+ });
265
+ return keys;
266
+ } else {
267
+ let keys = res.filter((key_value_pair) => {
268
+ return globMatch(pattern, key_value_pair.key);
269
+ });
270
+ return keys;
271
+ }
272
+ }
273
+
274
+ return res;
275
+ },
276
+ }).call(this, options);
277
+ };
278
+
279
+ // resolve to 'true' on success, or rejects with an error on failure
280
+ // will still resolve to 'true' if there are no keys
281
+ flush = utils.make_driver_method([], 'puter-kvstore', undefined, 'flush');
282
+
283
+ // clear is an alias for flush
284
+ clear = this.flush;
285
+ }
286
+
287
+ function globMatch(pattern, str) {
288
+ const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
289
+
290
+ let regexPattern = escapeRegExp(pattern)
291
+ .replace(/\\\*/g, '.*') // Replace * with .*
292
+ .replace(/\\\?/g, '.') // Replace ? with .
293
+ .replace(/\\\[/g, '[') // Replace [ with [
294
+ .replace(/\\\]/g, ']') // Replace ] with ]
295
+ .replace(/\\\^/g, '^'); // Replace ^ with ^
296
+
297
+ let re = new RegExp('^' + regexPattern + '$');
298
+ return re.test(str);
299
+ }
300
+
301
+ export default KV;
@@ -0,0 +1,95 @@
1
+ import * as utils from '../lib/utils.js'
2
+
3
+ class OS{
4
+ /**
5
+ * Creates a new instance with the given authentication token, API origin, and app ID,
6
+ *
7
+ * @class
8
+ * @param {string} authToken - Token used to authenticate the user.
9
+ * @param {string} APIOrigin - Origin of the API server. Used to build the API endpoint URLs.
10
+ * @param {string} appID - ID of the app to use.
11
+ */
12
+ constructor (context) {
13
+ this.authToken = context.authToken;
14
+ this.APIOrigin = context.APIOrigin;
15
+ this.appID = context.appID;
16
+ }
17
+
18
+ /**
19
+ * Sets a new authentication token.
20
+ *
21
+ * @param {string} authToken - The new authentication token.
22
+ * @memberof [OS]
23
+ * @returns {void}
24
+ */
25
+ setAuthToken (authToken) {
26
+ this.authToken = authToken;
27
+ }
28
+
29
+ /**
30
+ * Sets the API origin.
31
+ *
32
+ * @param {string} APIOrigin - The new API origin.
33
+ * @memberof [Apps]
34
+ * @returns {void}
35
+ */
36
+ setAPIOrigin (APIOrigin) {
37
+ this.APIOrigin = APIOrigin;
38
+ }
39
+
40
+ user = function(...args){
41
+ let options;
42
+
43
+ // If first argument is an object, it's the options
44
+ if (typeof args[0] === 'object' && args[0] !== null) {
45
+ options = args[0];
46
+ } else {
47
+ // Otherwise, we assume separate arguments are provided
48
+ options = {
49
+ success: args[0],
50
+ error: args[1],
51
+ };
52
+ }
53
+
54
+ let query = '';
55
+ if(options?.query){
56
+ query = '?' + new URLSearchParams(options.query).toString();
57
+ }
58
+
59
+ return new Promise((resolve, reject) => {
60
+ const xhr = utils.initXhr('/whoami' + query, this.APIOrigin, this.authToken, 'get');
61
+
62
+ // set up event handlers for load and error events
63
+ utils.setupXhrEventHandlers(xhr, options.success, options.error, resolve, reject);
64
+
65
+ xhr.send();
66
+ })
67
+ }
68
+
69
+ version = function(...args){
70
+ let options;
71
+
72
+ // If first argument is an object, it's the options
73
+ if (typeof args[0] === 'object' && args[0] !== null) {
74
+ options = args[0];
75
+ } else {
76
+ // Otherwise, we assume separate arguments are provided
77
+ options = {
78
+ success: args[0],
79
+ error: args[1],
80
+ // Add more if needed...
81
+ };
82
+ }
83
+
84
+ return new Promise((resolve, reject) => {
85
+ const xhr = utils.initXhr('/version', this.APIOrigin, this.authToken, 'get');
86
+
87
+ // set up event handlers for load and error events
88
+ utils.setupXhrEventHandlers(xhr, options.success, options.error, resolve, reject);
89
+
90
+ xhr.send();
91
+ })
92
+ }
93
+ }
94
+
95
+ export default OS
@@ -0,0 +1,109 @@
1
+ export default class Perms {
2
+ constructor (context) {
3
+ this.authToken = context.authToken;
4
+ this.APIOrigin = context.APIOrigin;
5
+ }
6
+ setAuthToken (authToken) {
7
+ this.authToken = authToken;
8
+ }
9
+ setAPIOrigin (APIOrigin) {
10
+ this.APIOrigin = APIOrigin;
11
+ }
12
+ async req_ (route, body) {
13
+ const resp = await fetch(
14
+ this.APIOrigin + route, {
15
+ method: body ? 'POST' : 'GET',
16
+ headers: {
17
+ Authorization: `Bearer ${this.authToken}`,
18
+ 'Content-Type': 'application/json',
19
+ },
20
+ ...(body ? { body: JSON.stringify(body) } : {}),
21
+ }
22
+ );
23
+ return await resp.json();
24
+ }
25
+
26
+ // Grant Permissions
27
+ async grantUser (target_username, permission) {
28
+ return await this.req_('/auth/grant-user-user', {
29
+ target_username, permission,
30
+ })
31
+ }
32
+
33
+ async grantGroup (group_uid, permission) {
34
+ return await this.req_('/auth/grant-user-group', {
35
+ group_uid, permission,
36
+ })
37
+ }
38
+
39
+ async grantApp (app_uid, permission) {
40
+ return await this.req_('/auth/grant-user-app', {
41
+ app_uid, permission,
42
+ })
43
+ }
44
+
45
+ async grantAppAnyUser (app_uid, permission) {
46
+ return await this.req_('/auth/grant-dev-app', {
47
+ app_uid, permission,
48
+ })
49
+ }
50
+
51
+ async grantOrigin (origin, permission) {
52
+ return await this.req_('/auth/grant-user-app', {
53
+ origin, permission,
54
+ })
55
+ }
56
+
57
+ // Revoke Permissions
58
+ async revokeUser (target_username, permission) {
59
+ return await this.req_('/auth/revoke-user-user', {
60
+ target_username, permission,
61
+ })
62
+ }
63
+
64
+ async revokeGroup (group_uid, permission) {
65
+ return await this.req_('/auth/revoke-user-group', {
66
+ group_uid, permission,
67
+ })
68
+ }
69
+
70
+ async revokeApp (app_uid, permission) {
71
+ return await this.req_('/auth/revoke-user-app', {
72
+ app_uid, permission,
73
+ })
74
+ }
75
+
76
+ async revokeAppAnyUser (app_uid, permission) {
77
+ return await this.req_('/auth/revoke-dev-app', {
78
+ app_uid, permission,
79
+ })
80
+ }
81
+
82
+ async revokeOrigin (origin, permission) {
83
+ return await this.req_('/auth/revoke-user-app', {
84
+ origin, permission,
85
+ })
86
+ }
87
+
88
+ // Group Management
89
+ async createGroup (metadata = {}, extra = {}) {
90
+ return await this.req_('/group/create', {
91
+ metadata, extra,
92
+ });
93
+ }
94
+ async addUsersToGroup (uid, usernames) {
95
+ return await this.req_('/group/add-users', {
96
+ uid,
97
+ users: usernames ?? [],
98
+ });
99
+ }
100
+ async removeUsersFromGroup (uid, usernames) {
101
+ return await this.req_('/group/remove-users', {
102
+ uid,
103
+ users: usernames ?? [],
104
+ });
105
+ }
106
+ async listGroups () {
107
+ return await this.req_('/group/list');
108
+ }
109
+ }