@heyputer/puter.js 2.0.1 → 2.0.3
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.js +4 -0
- package/package.json +4 -4
- package/src/bg.png +0 -0
- package/src/bg.webp +0 -0
- package/src/index.js +165 -165
- package/src/lib/APICallLogger.js +110 -0
- package/src/lib/EventListener.js +51 -0
- package/src/lib/RequestError.js +6 -0
- package/src/lib/filesystem/APIFS.js +73 -0
- package/src/lib/filesystem/CacheFS.js +243 -0
- package/src/lib/filesystem/PostMessageFS.js +40 -0
- package/src/lib/filesystem/definitions.js +39 -0
- package/src/lib/path.js +509 -0
- package/src/lib/polyfills/localStorage.js +92 -0
- package/src/lib/polyfills/xhrshim.js +233 -0
- package/src/lib/socket.io/socket.io.esm.min.js +7 -0
- package/src/lib/socket.io/socket.io.esm.min.js.map +1 -0
- package/src/lib/socket.io/socket.io.js +4385 -0
- package/src/lib/socket.io/socket.io.js.map +1 -0
- package/src/lib/socket.io/socket.io.min.js +7 -0
- package/src/lib/socket.io/socket.io.min.js.map +1 -0
- package/src/lib/socket.io/socket.io.msgpack.min.js +7 -0
- package/src/lib/socket.io/socket.io.msgpack.min.js.map +1 -0
- package/src/lib/utils.js +620 -0
- package/src/lib/xdrpc.js +104 -0
- package/src/modules/AI.js +680 -0
- package/src/modules/Apps.js +215 -0
- package/src/modules/Auth.js +171 -0
- package/src/modules/Debug.js +39 -0
- package/src/modules/Drivers.js +278 -0
- package/src/modules/FSItem.js +139 -0
- package/src/modules/FileSystem/index.js +187 -0
- package/src/modules/FileSystem/operations/copy.js +64 -0
- package/src/modules/FileSystem/operations/deleteFSEntry.js +59 -0
- package/src/modules/FileSystem/operations/getReadUrl.js +42 -0
- package/src/modules/FileSystem/operations/mkdir.js +62 -0
- package/src/modules/FileSystem/operations/move.js +75 -0
- package/src/modules/FileSystem/operations/read.js +46 -0
- package/src/modules/FileSystem/operations/readdir.js +102 -0
- package/src/modules/FileSystem/operations/rename.js +58 -0
- package/src/modules/FileSystem/operations/sign.js +103 -0
- package/src/modules/FileSystem/operations/space.js +40 -0
- package/src/modules/FileSystem/operations/stat.js +95 -0
- package/src/modules/FileSystem/operations/symlink.js +55 -0
- package/src/modules/FileSystem/operations/upload.js +440 -0
- package/src/modules/FileSystem/operations/write.js +65 -0
- package/src/modules/FileSystem/utils/getAbsolutePathForApp.js +21 -0
- package/src/modules/Hosting.js +138 -0
- package/src/modules/KV.js +301 -0
- package/src/modules/OS.js +95 -0
- package/src/modules/Perms.js +109 -0
- package/src/modules/PuterDialog.js +481 -0
- package/src/modules/Threads.js +75 -0
- package/src/modules/UI.js +1555 -0
- package/src/modules/Util.js +38 -0
- package/src/modules/Workers.js +120 -0
- package/src/modules/networking/PSocket.js +87 -0
- package/src/modules/networking/PTLS.js +100 -0
- package/src/modules/networking/PWispHandler.js +89 -0
- package/src/modules/networking/parsers.js +157 -0
- package/src/modules/networking/requests.js +282 -0
- package/src/safeLoadPuter.cjs +29 -0
- package/src/services/APIAccess.js +46 -0
- package/src/services/FSRelay.js +20 -0
- package/src/services/Filesystem.js +122 -0
- package/src/services/NoPuterYet.js +20 -0
- package/src/services/XDIncoming.js +44 -0
- package/index.d.ts +0 -479
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import * as utils from '../lib/utils.js'
|
|
2
|
+
|
|
3
|
+
class Apps{
|
|
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 [Apps]
|
|
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
|
+
list = async (...args) => {
|
|
41
|
+
let options = {};
|
|
42
|
+
|
|
43
|
+
// if args is a single object, assume it is the options object
|
|
44
|
+
if (typeof args[0] === 'object' && args[0] !== null) {
|
|
45
|
+
options.params = args[0];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
options.predicate = ['user-can-edit'];
|
|
49
|
+
|
|
50
|
+
return utils.make_driver_method(['uid'], 'puter-apps', undefined, 'select').call(this, options);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
create = async (...args) => {
|
|
54
|
+
let options = {};
|
|
55
|
+
// * allows for: puter.apps.new('example-app') *
|
|
56
|
+
if (typeof args[0] === 'string') {
|
|
57
|
+
let indexURL = args[1];
|
|
58
|
+
let title = args[2] ?? args[0];
|
|
59
|
+
|
|
60
|
+
options = {
|
|
61
|
+
object: {
|
|
62
|
+
name: args[0],
|
|
63
|
+
index_url: indexURL,
|
|
64
|
+
title: title
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// * allows for: puter.apps.new({name: 'example-app', indexURL: 'https://example.com'}) *
|
|
70
|
+
else if (typeof args[0] === 'object' && args[0] !== null) {
|
|
71
|
+
let options_raw = args[0];
|
|
72
|
+
|
|
73
|
+
options = {
|
|
74
|
+
object: {
|
|
75
|
+
name: options_raw.name,
|
|
76
|
+
index_url: options_raw.indexURL,
|
|
77
|
+
// title is optional only if name is provided.
|
|
78
|
+
// If title is provided, use it. If not, use name.
|
|
79
|
+
title: options_raw.title ?? options_raw.name,
|
|
80
|
+
description: options_raw.description,
|
|
81
|
+
icon: options_raw.icon,
|
|
82
|
+
maximize_on_start: options_raw.maximizeOnStart,
|
|
83
|
+
background: options_raw.background,
|
|
84
|
+
filetype_associations: options_raw.filetypeAssociations,
|
|
85
|
+
metadata: options_raw.metadata,
|
|
86
|
+
},
|
|
87
|
+
options: {
|
|
88
|
+
dedupe_name: options_raw.dedupeName ?? false,
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// name and indexURL are required
|
|
94
|
+
if(!options.object.name){
|
|
95
|
+
throw {
|
|
96
|
+
success: false,
|
|
97
|
+
error: {
|
|
98
|
+
code: 'invalid_request',
|
|
99
|
+
message: 'Name is required'
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if(!options.object.index_url){
|
|
104
|
+
throw {
|
|
105
|
+
success: false,
|
|
106
|
+
error: {
|
|
107
|
+
code: 'invalid_request',
|
|
108
|
+
message: 'Index URL is required'
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Call the original chat.complete method
|
|
114
|
+
return await utils.make_driver_method(['object'], 'puter-apps', undefined, 'create').call(this, options);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
update = async(...args) => {
|
|
118
|
+
let options = {};
|
|
119
|
+
|
|
120
|
+
// if there is one string argument, assume it is the app name
|
|
121
|
+
// * allows for: puter.apps.update('example-app') *
|
|
122
|
+
if (Array.isArray(args) && typeof args[0] === 'string') {
|
|
123
|
+
let object_raw = args[1];
|
|
124
|
+
let object = {
|
|
125
|
+
name: object_raw.name,
|
|
126
|
+
index_url: object_raw.indexURL,
|
|
127
|
+
title: object_raw.title,
|
|
128
|
+
description: object_raw.description,
|
|
129
|
+
icon: object_raw.icon,
|
|
130
|
+
maximize_on_start: object_raw.maximizeOnStart,
|
|
131
|
+
background: object_raw.background,
|
|
132
|
+
filetype_associations: object_raw.filetypeAssociations,
|
|
133
|
+
metadata: object_raw.metadata,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
options = { id: { name: args[0]}, object: object};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Call the original chat.complete method
|
|
140
|
+
return await utils.make_driver_method(['object'], 'puter-apps', undefined, 'update').call(this, options);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
get = async(...args) => {
|
|
144
|
+
let options = {};
|
|
145
|
+
|
|
146
|
+
// if there is one string argument, assume it is the app name
|
|
147
|
+
// * allows for: puter.apps.get('example-app') *
|
|
148
|
+
if (Array.isArray(args) && typeof args[0] === 'string') {
|
|
149
|
+
// if second argument is an object, assume it is the options object
|
|
150
|
+
if (typeof args[1] === 'object' && args[1] !== null) {
|
|
151
|
+
options.params = args[1];
|
|
152
|
+
}
|
|
153
|
+
// name
|
|
154
|
+
options.id = {name: args[0]};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// if first argument is an object, assume it is the options object
|
|
158
|
+
if (typeof args[0] === 'object' && args[0] !== null) {
|
|
159
|
+
options.params = args[0];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return utils.make_driver_method(['uid'], 'puter-apps', undefined, 'read').call(this, options);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
delete = async(...args) => {
|
|
166
|
+
let options = {};
|
|
167
|
+
// if there is one string argument, assume it is the app name
|
|
168
|
+
// * allows for: puter.apps.get('example-app') *
|
|
169
|
+
if (Array.isArray(args) && typeof args[0] === 'string') {
|
|
170
|
+
options = { id: {name: args[0]}};
|
|
171
|
+
}
|
|
172
|
+
return utils.make_driver_method(['uid'], 'puter-apps', undefined, 'delete').call(this, options);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
getDeveloperProfile = function(...args){
|
|
176
|
+
let options;
|
|
177
|
+
|
|
178
|
+
// If first argument is an object, it's the options
|
|
179
|
+
if (typeof args[0] === 'object' && args[0] !== null) {
|
|
180
|
+
options = args[0];
|
|
181
|
+
} else {
|
|
182
|
+
// Otherwise, we assume separate arguments are provided
|
|
183
|
+
options = {
|
|
184
|
+
success: args[0],
|
|
185
|
+
error: args[1],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return new Promise((resolve, reject) => {
|
|
190
|
+
let options;
|
|
191
|
+
|
|
192
|
+
// If first argument is an object, it's the options
|
|
193
|
+
if (typeof args[0] === 'object' && args[0] !== null) {
|
|
194
|
+
options = args[0];
|
|
195
|
+
} else {
|
|
196
|
+
// Otherwise, we assume separate arguments are provided
|
|
197
|
+
options = {
|
|
198
|
+
success: args[0],
|
|
199
|
+
error: args[1],
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return new Promise((resolve, reject) => {
|
|
204
|
+
const xhr = utils.initXhr('/get-dev-profile', puter.APIOrigin, puter.authToken, 'get');
|
|
205
|
+
|
|
206
|
+
// set up event handlers for load and error events
|
|
207
|
+
utils.setupXhrEventHandlers(xhr, options.success, options.error, resolve, reject);
|
|
208
|
+
|
|
209
|
+
xhr.send();
|
|
210
|
+
})
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export default Apps;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import * as utils from '../lib/utils.js'
|
|
2
|
+
|
|
3
|
+
class Auth{
|
|
4
|
+
// Used to generate a unique message id for each message sent to the host environment
|
|
5
|
+
// we start from 1 because 0 is falsy and we want to avoid that for the message id
|
|
6
|
+
#messageID = 1;
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Creates a new instance with the given authentication token, API origin, and app ID,
|
|
11
|
+
*
|
|
12
|
+
* @class
|
|
13
|
+
* @param {string} authToken - Token used to authenticate the user.
|
|
14
|
+
* @param {string} APIOrigin - Origin of the API server. Used to build the API endpoint URLs.
|
|
15
|
+
* @param {string} appID - ID of the app to use.
|
|
16
|
+
*/
|
|
17
|
+
constructor (context) {
|
|
18
|
+
this.authToken = context.authToken;
|
|
19
|
+
this.APIOrigin = context.APIOrigin;
|
|
20
|
+
this.appID = context.appID;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Sets a new authentication token.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} authToken - The new authentication token.
|
|
27
|
+
* @memberof [Auth]
|
|
28
|
+
* @returns {void}
|
|
29
|
+
*/
|
|
30
|
+
setAuthToken (authToken) {
|
|
31
|
+
this.authToken = authToken;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Sets the API origin.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} APIOrigin - The new API origin.
|
|
38
|
+
* @memberof [Auth]
|
|
39
|
+
* @returns {void}
|
|
40
|
+
*/
|
|
41
|
+
setAPIOrigin (APIOrigin) {
|
|
42
|
+
this.APIOrigin = APIOrigin;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
signIn = (options) =>{
|
|
46
|
+
options = options || {};
|
|
47
|
+
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
let msg_id = this.#messageID++;
|
|
50
|
+
let w = 600;
|
|
51
|
+
let h = 600;
|
|
52
|
+
let title = 'Puter';
|
|
53
|
+
var left = (screen.width/2)-(w/2);
|
|
54
|
+
var top = (screen.height/2)-(h/2);
|
|
55
|
+
|
|
56
|
+
// 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);
|
|
60
|
+
|
|
61
|
+
// Set up interval to check if popup was closed
|
|
62
|
+
const checkClosed = setInterval(() => {
|
|
63
|
+
if (popup.closed) {
|
|
64
|
+
clearInterval(checkClosed);
|
|
65
|
+
// Remove the message listener
|
|
66
|
+
window.removeEventListener('message', messageHandler);
|
|
67
|
+
reject({ error: 'auth_window_closed', msg: 'Authentication window was closed by the user without completing the process.' });
|
|
68
|
+
}
|
|
69
|
+
}, 100);
|
|
70
|
+
|
|
71
|
+
function messageHandler(e) {
|
|
72
|
+
if(e.data.msg_id == msg_id){
|
|
73
|
+
// Clear the interval since we got a response
|
|
74
|
+
clearInterval(checkClosed);
|
|
75
|
+
|
|
76
|
+
// remove redundant attributes
|
|
77
|
+
delete e.data.msg_id;
|
|
78
|
+
delete e.data.msg;
|
|
79
|
+
|
|
80
|
+
if(e.data.success){
|
|
81
|
+
// set the auth token
|
|
82
|
+
puter.setAuthToken(e.data.token);
|
|
83
|
+
|
|
84
|
+
resolve(e.data);
|
|
85
|
+
}else
|
|
86
|
+
reject(e.data);
|
|
87
|
+
|
|
88
|
+
// delete the listener
|
|
89
|
+
window.removeEventListener('message', messageHandler);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
window.addEventListener('message', messageHandler);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
isSignedIn = () =>{
|
|
98
|
+
if(puter.authToken)
|
|
99
|
+
return true;
|
|
100
|
+
else
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getUser = function(...args){
|
|
105
|
+
let options;
|
|
106
|
+
|
|
107
|
+
// If first argument is an object, it's the options
|
|
108
|
+
if (typeof args[0] === 'object' && args[0] !== null) {
|
|
109
|
+
options = args[0];
|
|
110
|
+
} else {
|
|
111
|
+
// Otherwise, we assume separate arguments are provided
|
|
112
|
+
options = {
|
|
113
|
+
success: args[0],
|
|
114
|
+
error: args[1],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
const xhr = utils.initXhr('/whoami', puter.APIOrigin, puter.authToken, 'get');
|
|
120
|
+
|
|
121
|
+
// set up event handlers for load and error events
|
|
122
|
+
utils.setupXhrEventHandlers(xhr, options.success, options.error, resolve, reject);
|
|
123
|
+
|
|
124
|
+
xhr.send();
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
signOut = () =>{
|
|
129
|
+
puter.resetAuthToken();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async whoami () {
|
|
133
|
+
try {
|
|
134
|
+
const resp = await fetch(this.APIOrigin + '/whoami', {
|
|
135
|
+
headers: {
|
|
136
|
+
Authorization: `Bearer ${this.authToken}`
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const result = await resp.json();
|
|
141
|
+
|
|
142
|
+
// Log the response
|
|
143
|
+
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
144
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
145
|
+
service: 'auth',
|
|
146
|
+
operation: 'whoami',
|
|
147
|
+
params: {},
|
|
148
|
+
result: result
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return result;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
// Log the error
|
|
155
|
+
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
156
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
157
|
+
service: 'auth',
|
|
158
|
+
operation: 'whoami',
|
|
159
|
+
params: {},
|
|
160
|
+
error: {
|
|
161
|
+
message: error.message || error.toString(),
|
|
162
|
+
stack: error.stack
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export default Auth
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export class Debug {
|
|
2
|
+
constructor (context, parameters) {
|
|
3
|
+
this.context = context;
|
|
4
|
+
this.parameters = parameters;
|
|
5
|
+
|
|
6
|
+
this._init();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
_init () {
|
|
10
|
+
// Check query parameter 'enabled_logs'
|
|
11
|
+
const url = new URL(location.href);
|
|
12
|
+
let enabled_logs = url.searchParams.get('enabled_logs');
|
|
13
|
+
if ( ! enabled_logs ) enabled_logs = '';
|
|
14
|
+
enabled_logs = enabled_logs.split(';');
|
|
15
|
+
for ( const category of enabled_logs ) {
|
|
16
|
+
if ( category === '' ) continue;
|
|
17
|
+
this.context.puter.logger.on(category);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
globalThis.addEventListener('message', async e => {
|
|
21
|
+
// Ensure message is from parent window
|
|
22
|
+
if ( e.source !== globalThis.parent ) return;
|
|
23
|
+
// (parent window is allowed to be anything)
|
|
24
|
+
|
|
25
|
+
// Check if it's a debug message
|
|
26
|
+
if ( ! e.data.$ ) return;
|
|
27
|
+
if ( e.data.$ !== 'puterjs-debug' ) return;
|
|
28
|
+
|
|
29
|
+
// It's okay to log this; it will only show if a
|
|
30
|
+
// developer does something in the console.
|
|
31
|
+
console.log('Got a puter.js debug event!', e.data);
|
|
32
|
+
|
|
33
|
+
if ( e.data.cmd === 'log.on' ) {
|
|
34
|
+
console.log('Got instruction to turn logs on!');
|
|
35
|
+
this.context.puter.logger.on(e.data.category);
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
class FetchDriverCallBackend {
|
|
2
|
+
constructor ({ context }) {
|
|
3
|
+
this.context = context;
|
|
4
|
+
this.response_handlers = this.constructor.response_handlers;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
static response_handlers = {
|
|
8
|
+
'application/x-ndjson': async resp => {
|
|
9
|
+
const Stream = async function* Stream (readableStream) {
|
|
10
|
+
const reader = readableStream.getReader();
|
|
11
|
+
let value, done;
|
|
12
|
+
while ( ! done ) {
|
|
13
|
+
({ value, done } = await reader.read());
|
|
14
|
+
if ( done ) break;
|
|
15
|
+
const parts = (new TextDecoder().decode(value).split('\n'));
|
|
16
|
+
for ( const part of parts ) {
|
|
17
|
+
if ( part.trim() === '' ) continue;
|
|
18
|
+
yield JSON.parse(part);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return Stream(resp.body);
|
|
24
|
+
},
|
|
25
|
+
'application/json': async resp => {
|
|
26
|
+
return await resp.json();
|
|
27
|
+
},
|
|
28
|
+
'application/octet-stream': async resp => {
|
|
29
|
+
return await resp.blob();
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async call ({ driver, method_name, parameters }) {
|
|
34
|
+
try {
|
|
35
|
+
const resp = await fetch(`${this.context.APIOrigin}/drivers/call`, {
|
|
36
|
+
headers: {
|
|
37
|
+
'Content-Type': 'text/plain;actually=json',
|
|
38
|
+
},
|
|
39
|
+
method: 'POST',
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
'interface': driver.iface_name,
|
|
42
|
+
...(driver.service_name
|
|
43
|
+
? { service: driver.service_name }
|
|
44
|
+
: {}),
|
|
45
|
+
method: method_name,
|
|
46
|
+
args: parameters,
|
|
47
|
+
auth_token: this.context.authToken
|
|
48
|
+
}),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const content_type = resp.headers.get('content-type')
|
|
52
|
+
.split(';')[0].trim(); // TODO: parser for Content-Type
|
|
53
|
+
const handler = this.response_handlers[content_type];
|
|
54
|
+
if ( ! handler ) {
|
|
55
|
+
const msg = `unrecognized content type: ${content_type}`;
|
|
56
|
+
console.error(msg);
|
|
57
|
+
console.error('creating blob so dev tools shows response...');
|
|
58
|
+
await resp.blob();
|
|
59
|
+
|
|
60
|
+
// Log the error
|
|
61
|
+
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
62
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
63
|
+
service: 'drivers',
|
|
64
|
+
operation: `${driver.iface_name}::${method_name}`,
|
|
65
|
+
params: { interface: driver.iface_name, driver: driver.service_name || driver.iface_name, method: method_name, args: parameters },
|
|
66
|
+
error: { message: msg }
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
throw new Error(msg);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const result = await handler(resp);
|
|
74
|
+
|
|
75
|
+
// Log the successful response
|
|
76
|
+
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
77
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
78
|
+
service: 'drivers',
|
|
79
|
+
operation: `${driver.iface_name}::${method_name}`,
|
|
80
|
+
params: { interface: driver.iface_name, driver: driver.service_name || driver.iface_name, method: method_name, args: parameters },
|
|
81
|
+
result: result
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return result;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// Log unexpected errors
|
|
88
|
+
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
89
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
90
|
+
service: 'drivers',
|
|
91
|
+
operation: `${driver.iface_name}::${method_name}`,
|
|
92
|
+
params: { interface: driver.iface_name, driver: driver.service_name || driver.iface_name, method: method_name, args: parameters },
|
|
93
|
+
error: {
|
|
94
|
+
message: error.message || error.toString(),
|
|
95
|
+
stack: error.stack
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class Driver {
|
|
105
|
+
constructor ({
|
|
106
|
+
iface,
|
|
107
|
+
iface_name,
|
|
108
|
+
service_name,
|
|
109
|
+
call_backend,
|
|
110
|
+
}) {
|
|
111
|
+
this.iface = iface;
|
|
112
|
+
this.iface_name = iface_name;
|
|
113
|
+
this.service_name = service_name;
|
|
114
|
+
this.call_backend = call_backend;
|
|
115
|
+
}
|
|
116
|
+
async call (method_name, parameters) {
|
|
117
|
+
return await this.call_backend.call({
|
|
118
|
+
driver: this,
|
|
119
|
+
method_name,
|
|
120
|
+
parameters,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
class Drivers {
|
|
126
|
+
/**
|
|
127
|
+
* Creates a new instance with the given authentication token, API origin, and app ID,
|
|
128
|
+
*
|
|
129
|
+
* @class
|
|
130
|
+
* @param {string} authToken - Token used to authenticate the user.
|
|
131
|
+
* @param {string} APIOrigin - Origin of the API server. Used to build the API endpoint URLs.
|
|
132
|
+
* @param {string} appID - ID of the app to use.
|
|
133
|
+
*/
|
|
134
|
+
constructor (context) {
|
|
135
|
+
this.authToken = context.authToken;
|
|
136
|
+
this.APIOrigin = context.APIOrigin;
|
|
137
|
+
this.appID = context.appID;
|
|
138
|
+
|
|
139
|
+
// Driver-specific
|
|
140
|
+
this.drivers_ = {};
|
|
141
|
+
|
|
142
|
+
// TODO: replace with `context` from constructor and test site login
|
|
143
|
+
this.context = {};
|
|
144
|
+
Object.defineProperty(this.context, 'authToken', {
|
|
145
|
+
get: () => this.authToken,
|
|
146
|
+
});
|
|
147
|
+
Object.defineProperty(this.context, 'APIOrigin', {
|
|
148
|
+
get: () => this.APIOrigin,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
_init ({ puter }) {
|
|
153
|
+
puter.call = this.call.bind(this);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Sets a new authentication token and resets the socket connection with the updated token, if applicable.
|
|
158
|
+
*
|
|
159
|
+
* @param {string} authToken - The new authentication token.
|
|
160
|
+
* @memberof [AI]
|
|
161
|
+
* @returns {void}
|
|
162
|
+
*/
|
|
163
|
+
setAuthToken (authToken) {
|
|
164
|
+
this.authToken = authToken;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Sets the API origin.
|
|
169
|
+
*
|
|
170
|
+
* @param {string} APIOrigin - The new API origin.
|
|
171
|
+
* @memberof [AI]
|
|
172
|
+
* @returns {void}
|
|
173
|
+
*/
|
|
174
|
+
setAPIOrigin (APIOrigin) {
|
|
175
|
+
this.APIOrigin = APIOrigin;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async list () {
|
|
179
|
+
try {
|
|
180
|
+
const resp = await fetch(`${this.APIOrigin}/lsmod`, {
|
|
181
|
+
headers: {
|
|
182
|
+
Authorization: 'Bearer ' + this.authToken,
|
|
183
|
+
},
|
|
184
|
+
method: 'POST'
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const list = await resp.json();
|
|
188
|
+
|
|
189
|
+
// Log the response
|
|
190
|
+
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
191
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
192
|
+
service: 'drivers',
|
|
193
|
+
operation: 'list',
|
|
194
|
+
params: {},
|
|
195
|
+
result: list.interfaces
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return list.interfaces;
|
|
200
|
+
} catch (error) {
|
|
201
|
+
// Log the error
|
|
202
|
+
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
203
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
204
|
+
service: 'drivers',
|
|
205
|
+
operation: 'list',
|
|
206
|
+
params: {},
|
|
207
|
+
error: {
|
|
208
|
+
message: error.message || error.toString(),
|
|
209
|
+
stack: error.stack
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async get (iface_name, service_name) {
|
|
218
|
+
if ( ! service_name ) service_name = iface_name;
|
|
219
|
+
const key = `${iface_name}:${service_name}`;
|
|
220
|
+
if ( this.drivers_[key] ) return this.drivers_[key];
|
|
221
|
+
|
|
222
|
+
// const interfaces = await this.list();
|
|
223
|
+
// if ( ! interfaces[iface_name] ) {
|
|
224
|
+
// throw new Error(`Interface ${iface_name} not found`);
|
|
225
|
+
// }
|
|
226
|
+
|
|
227
|
+
return this.drivers_[key] = new Driver ({
|
|
228
|
+
call_backend: new FetchDriverCallBackend({
|
|
229
|
+
context: this.context,
|
|
230
|
+
}),
|
|
231
|
+
// iface: interfaces[iface_name],
|
|
232
|
+
iface_name,
|
|
233
|
+
service_name,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async call (...a) {
|
|
238
|
+
let iface_name, service_name, method_name, parameters;
|
|
239
|
+
|
|
240
|
+
// Services with the same name as an interface they implement
|
|
241
|
+
// are considered the default implementation for that interface.
|
|
242
|
+
//
|
|
243
|
+
// A method with the same name as the interface and service it is
|
|
244
|
+
// called on can be left unspecified in a driver call through puter.js.
|
|
245
|
+
//
|
|
246
|
+
// For example:
|
|
247
|
+
// puter.drivers.call('ipgeo', { ip: '1.2.3.4' });
|
|
248
|
+
//
|
|
249
|
+
// Is the same as:
|
|
250
|
+
// puter.drivers.call('ipgeo', 'ipgeo', 'ipgeo', { ip: '1.2.3.4' })
|
|
251
|
+
//
|
|
252
|
+
// This is commonly the case when an interface only exists to
|
|
253
|
+
// connect a particular service to the drivers API. In this case,
|
|
254
|
+
// the interface might not specify the structure of the response
|
|
255
|
+
// because it is only intended for that specific integration
|
|
256
|
+
// (and that integration alone is responsible for avoiding regressions)
|
|
257
|
+
|
|
258
|
+
// interface name, service name, method name, parameters
|
|
259
|
+
if ( a.length === 4 ) {
|
|
260
|
+
([iface_name, service_name, method_name, parameters] = a);
|
|
261
|
+
}
|
|
262
|
+
// interface name, method name, parameters
|
|
263
|
+
else if ( a.length === 3 ) {
|
|
264
|
+
([iface_name, method_name, parameters] = a);
|
|
265
|
+
}
|
|
266
|
+
// interface name, parameters
|
|
267
|
+
else if ( a.length === 2 ) {
|
|
268
|
+
([iface_name, parameters] = a);
|
|
269
|
+
method_name = iface_name;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const driver = await this.get(iface_name, service_name);
|
|
273
|
+
return await driver.call(method_name, parameters);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export default Drivers;
|