@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,282 @@
|
|
|
1
|
+
// SO: https://stackoverflow.com/a/76332760/ under CC BY-SA 4.0
|
|
2
|
+
function mergeUint8Arrays(...arrays) {
|
|
3
|
+
const totalSize = arrays.reduce((acc, e) => acc + e.length, 0);
|
|
4
|
+
const merged = new Uint8Array(totalSize);
|
|
5
|
+
|
|
6
|
+
arrays.forEach((array, i, arrays) => {
|
|
7
|
+
const offset = arrays.slice(0, i).reduce((acc, e) => acc + e.length, 0);
|
|
8
|
+
merged.set(array, offset);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
return merged;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function parseHTTPHead(head) {
|
|
15
|
+
const lines = head.split("\r\n");
|
|
16
|
+
|
|
17
|
+
const firstLine = lines.shift().split(" ");
|
|
18
|
+
const status = Number(firstLine[1]);
|
|
19
|
+
const statusText = firstLine.slice(2).join(" ") || "";
|
|
20
|
+
|
|
21
|
+
const headersArray = [];
|
|
22
|
+
for (const header of lines) {
|
|
23
|
+
const splitHeaders = header.split(": ");
|
|
24
|
+
const key = splitHeaders[0];
|
|
25
|
+
const value = splitHeaders.slice(1).join(": ");
|
|
26
|
+
headersArray.push([key, value]);
|
|
27
|
+
}
|
|
28
|
+
new Headers(headersArray);
|
|
29
|
+
return { headers: new Headers(headersArray), statusText, status };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Trivial stream based HTTP 1.1 client
|
|
33
|
+
// TODO optional redirect handling
|
|
34
|
+
|
|
35
|
+
export function pFetch(...args) {
|
|
36
|
+
return new Promise(async (res, rej) => {
|
|
37
|
+
try {
|
|
38
|
+
const reqObj = new Request(...args);
|
|
39
|
+
const parsedURL = new URL(reqObj.url);
|
|
40
|
+
let headers = new Headers(reqObj.headers); // Make a headers object we can modify
|
|
41
|
+
|
|
42
|
+
// Socket creation: regular for HTTP, TLS for https
|
|
43
|
+
let socket;
|
|
44
|
+
if (parsedURL.protocol === "http:") {
|
|
45
|
+
socket = new puter.net.Socket(
|
|
46
|
+
parsedURL.hostname,
|
|
47
|
+
parsedURL.port || 80,
|
|
48
|
+
);
|
|
49
|
+
} else if (parsedURL.protocol === "https:") {
|
|
50
|
+
socket = new puter.net.tls.TLSSocket(
|
|
51
|
+
parsedURL.hostname,
|
|
52
|
+
parsedURL.port || 443,
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
const errorMsg = `Failed to fetch. URL scheme "${parsedURL.protocol}" is not supported.`;
|
|
56
|
+
|
|
57
|
+
// Log the error
|
|
58
|
+
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
59
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
60
|
+
service: 'network',
|
|
61
|
+
operation: 'pFetch',
|
|
62
|
+
params: { url: reqObj.url, method: reqObj.method },
|
|
63
|
+
error: { message: errorMsg }
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
rej(errorMsg);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Sending default UA
|
|
72
|
+
if (!headers.get("user-agent")) {
|
|
73
|
+
headers.set("user-agent", navigator.userAgent);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let reqHead = `${reqObj.method} ${parsedURL.pathname}${parsedURL.search} HTTP/1.1\r\nHost: ${parsedURL.host}\r\nConnection: close\r\n`;
|
|
77
|
+
for (const [key, value] of headers) {
|
|
78
|
+
reqHead += `${key}: ${value}\r\n`;
|
|
79
|
+
}
|
|
80
|
+
let requestBody;
|
|
81
|
+
if (reqObj.body) {
|
|
82
|
+
requestBody = new Uint8Array(await reqObj.arrayBuffer());
|
|
83
|
+
// If we have a body, we need to set the content length
|
|
84
|
+
if (!headers.has("content-length")) {
|
|
85
|
+
headers.set("content-length", requestBody.length);
|
|
86
|
+
} else if (
|
|
87
|
+
headers.get("content-length") !== String(requestBody.length)
|
|
88
|
+
) {
|
|
89
|
+
return rej(
|
|
90
|
+
"Content-Length header does not match the body length. Please check your request.",
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
reqHead += `Content-Length: ${requestBody.length}\r\n`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
reqHead += "\r\n";
|
|
97
|
+
|
|
98
|
+
socket.on("open", async () => {
|
|
99
|
+
socket.write(reqHead); // Send headers
|
|
100
|
+
if (requestBody) {
|
|
101
|
+
socket.write(requestBody); // Send body if present
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
const decoder = new TextDecoder();
|
|
105
|
+
let responseHead = "";
|
|
106
|
+
let dataOffset = -1;
|
|
107
|
+
const fullDataParts = [];
|
|
108
|
+
let responseReturned = false;
|
|
109
|
+
let contentLength = -1;
|
|
110
|
+
let ingestedContent = 0;
|
|
111
|
+
let chunkedTransfer = false;
|
|
112
|
+
let currentChunkLeft = -1;
|
|
113
|
+
let buffer = new Uint8Array(0);
|
|
114
|
+
|
|
115
|
+
const outStream = new ReadableStream({
|
|
116
|
+
start(controller) {
|
|
117
|
+
// This is annoyingly long
|
|
118
|
+
function parseIncomingChunk(data) {
|
|
119
|
+
// append new data to our rolling buffer
|
|
120
|
+
const tmp = new Uint8Array(buffer.length + data.length);
|
|
121
|
+
tmp.set(buffer, 0);
|
|
122
|
+
tmp.set(data, buffer.length);
|
|
123
|
+
buffer = tmp;
|
|
124
|
+
|
|
125
|
+
// pull out as many complete chunks (or headers) as we can
|
|
126
|
+
while (true) {
|
|
127
|
+
if (currentChunkLeft > 0) {
|
|
128
|
+
// we’re in the middle of reading a chunk body
|
|
129
|
+
// need size + 2 bytes (for trailing \r\n)
|
|
130
|
+
if (buffer.length >= currentChunkLeft + 2) {
|
|
131
|
+
// full body + CRLF available
|
|
132
|
+
const chunk = buffer.slice(0, currentChunkLeft);
|
|
133
|
+
controller.enqueue(chunk);
|
|
134
|
+
|
|
135
|
+
// strip body + CRLF and reset for next header
|
|
136
|
+
buffer = buffer.slice(currentChunkLeft + 2);
|
|
137
|
+
currentChunkLeft = 0;
|
|
138
|
+
} else {
|
|
139
|
+
// only a partial body available
|
|
140
|
+
controller.enqueue(buffer);
|
|
141
|
+
currentChunkLeft -= buffer.length;
|
|
142
|
+
buffer = new Uint8Array(0);
|
|
143
|
+
break; // wait for more data
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
// we need to parse the next size line
|
|
147
|
+
// find the first "\r\n"
|
|
148
|
+
let idx = -1;
|
|
149
|
+
for (let i = 0; i + 1 < buffer.length; i++) {
|
|
150
|
+
if (
|
|
151
|
+
buffer[i] === 0x0d &&
|
|
152
|
+
buffer[i + 1] === 0x0a
|
|
153
|
+
) {
|
|
154
|
+
idx = i;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (idx < 0) {
|
|
159
|
+
// we don’t yet have a full size line
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// decode just the size line as ASCII hex
|
|
164
|
+
const sizeText = decoder
|
|
165
|
+
.decode(buffer.slice(0, idx))
|
|
166
|
+
.trim();
|
|
167
|
+
currentChunkLeft = parseInt(sizeText, 16);
|
|
168
|
+
if (isNaN(currentChunkLeft)) {
|
|
169
|
+
controller.error(
|
|
170
|
+
"Invalid chunk length from server",
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
// strip off the size line + CRLF
|
|
174
|
+
buffer = buffer.slice(idx + 2);
|
|
175
|
+
|
|
176
|
+
// zero-length => end of stream
|
|
177
|
+
if (currentChunkLeft === 0) {
|
|
178
|
+
responseReturned = true;
|
|
179
|
+
controller.close();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
socket.on("data", (data) => {
|
|
186
|
+
// Dataoffset is set to another value once head is returned, its safe to assume all remaining data is body
|
|
187
|
+
if (dataOffset !== -1 && !chunkedTransfer) {
|
|
188
|
+
controller.enqueue(data);
|
|
189
|
+
ingestedContent += data.length;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// We dont have the full responseHead yet
|
|
193
|
+
if (dataOffset === -1) {
|
|
194
|
+
fullDataParts.push(data);
|
|
195
|
+
responseHead += decoder.decode(data, { stream: true });
|
|
196
|
+
}
|
|
197
|
+
if (chunkedTransfer) {
|
|
198
|
+
parseIncomingChunk(data);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// See if we have the HEAD of an HTTP/1.1 yet
|
|
202
|
+
if (responseHead.indexOf("\r\n\r\n") !== -1) {
|
|
203
|
+
dataOffset = responseHead.indexOf("\r\n\r\n");
|
|
204
|
+
responseHead = responseHead.slice(0, dataOffset);
|
|
205
|
+
const parsedHead = parseHTTPHead(responseHead);
|
|
206
|
+
contentLength = Number(
|
|
207
|
+
parsedHead.headers.get("content-length"),
|
|
208
|
+
);
|
|
209
|
+
chunkedTransfer =
|
|
210
|
+
parsedHead.headers.get("transfer-encoding") ===
|
|
211
|
+
"chunked";
|
|
212
|
+
|
|
213
|
+
// Log the response
|
|
214
|
+
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
215
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
216
|
+
service: 'network',
|
|
217
|
+
operation: 'pFetch',
|
|
218
|
+
params: { url: reqObj.url, method: reqObj.method },
|
|
219
|
+
result: { status: parsedHead.status, statusText: parsedHead.statusText }
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Return initial response object
|
|
224
|
+
res(new Response(outStream, parsedHead));
|
|
225
|
+
|
|
226
|
+
const residualBody = mergeUint8Arrays(
|
|
227
|
+
...fullDataParts,
|
|
228
|
+
).slice(dataOffset + 4);
|
|
229
|
+
if (!chunkedTransfer) {
|
|
230
|
+
// Add any content we have but isn't part of the head into the body stream
|
|
231
|
+
ingestedContent += residualBody.length;
|
|
232
|
+
controller.enqueue(residualBody);
|
|
233
|
+
} else {
|
|
234
|
+
parseIncomingChunk(residualBody);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (
|
|
239
|
+
contentLength !== -1 &&
|
|
240
|
+
ingestedContent === contentLength &&
|
|
241
|
+
!chunkedTransfer
|
|
242
|
+
) {
|
|
243
|
+
// Work around for the close bug for compliant HTTP/1.1 servers
|
|
244
|
+
if (!responseReturned) {
|
|
245
|
+
responseReturned = true;
|
|
246
|
+
controller.close();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
socket.on("close", () => {
|
|
251
|
+
if (!responseReturned) {
|
|
252
|
+
responseReturned = true;
|
|
253
|
+
controller.close();
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
socket.on("error", (reason) => {
|
|
257
|
+
// Log the error
|
|
258
|
+
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
259
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
260
|
+
service: 'network',
|
|
261
|
+
operation: 'pFetch',
|
|
262
|
+
params: { url: reqObj.url, method: reqObj.method },
|
|
263
|
+
error: { message: "Socket errored with the following reason: " + reason }
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
rej("Socket errored with the following reason: " + reason);
|
|
267
|
+
});
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
} catch (e) {
|
|
271
|
+
// Log unexpected errors
|
|
272
|
+
if (globalThis.puter?.apiCallLogger?.isEnabled()) {
|
|
273
|
+
globalThis.puter.apiCallLogger.logRequest({
|
|
274
|
+
service: 'network',
|
|
275
|
+
operation: 'pFetch',
|
|
276
|
+
params: { url: reqObj.url, method: reqObj.method },
|
|
277
|
+
error: { message: e.message || e.toString(), stack: e.stack }
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
rej(e);
|
|
281
|
+
}});
|
|
282
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const readFileSync = require('node:fs');
|
|
2
|
+
const vm = require('node:vm');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Method for loading puter.js in Node.js environment
|
|
6
|
+
* @param {string} authToken - Optional auth token to initialize puter with
|
|
7
|
+
* @returns {Promise<import('../index').Puter>} The `puter` object from puter.js
|
|
8
|
+
*/
|
|
9
|
+
const safeLoadPuterJs = (authToken) => {
|
|
10
|
+
|
|
11
|
+
const goodContext = {};
|
|
12
|
+
Object.getOwnPropertyNames(globalThis).forEach(name => {
|
|
13
|
+
try {
|
|
14
|
+
goodContext[name] = globalThis[name];
|
|
15
|
+
} catch {
|
|
16
|
+
// silent fail
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
goodContext.globalThis = goodContext;
|
|
20
|
+
const code = readFileSync(`${globalThis.__dirname}/../dist/puter.js`, 'utf8');
|
|
21
|
+
const context = vm.createContext(goodContext);
|
|
22
|
+
vm.runInNewContext(code, context);
|
|
23
|
+
if ( authToken ) {
|
|
24
|
+
goodContext.puter.setAuthToken(authToken);
|
|
25
|
+
}
|
|
26
|
+
return goodContext.puter;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
module.exports = { safeLoadPuterJs };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import putility from "@heyputer/putility";
|
|
2
|
+
|
|
3
|
+
const { TTopics } = putility.traits;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Manages the auth token and origin used to communicate with
|
|
7
|
+
* Puter's API
|
|
8
|
+
*/
|
|
9
|
+
export class APIAccessService extends putility.concepts.Service {
|
|
10
|
+
static TOPICS = ['update'];
|
|
11
|
+
|
|
12
|
+
static PROPERTIES = {
|
|
13
|
+
auth_token: {
|
|
14
|
+
post_set (v) {
|
|
15
|
+
this.as(TTopics).pub('update');
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
api_origin: {
|
|
19
|
+
post_set () {
|
|
20
|
+
this.as(TTopics).pub('update');
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// TODO: inconsistent! Update all dependents.
|
|
26
|
+
get_api_info () {
|
|
27
|
+
const self = this;
|
|
28
|
+
const o = {};
|
|
29
|
+
[
|
|
30
|
+
['auth_token','auth_token'],
|
|
31
|
+
['authToken','auth_token'],
|
|
32
|
+
['APIOrigin','api_origin'],
|
|
33
|
+
['api_origin','api_origin'],
|
|
34
|
+
].forEach(([k1,k2]) => {
|
|
35
|
+
Object.defineProperty(o, k1, {
|
|
36
|
+
get () {
|
|
37
|
+
return self[k2];
|
|
38
|
+
},
|
|
39
|
+
set (v) {
|
|
40
|
+
return self;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
return o;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import putility from '@heyputer/putility';
|
|
2
|
+
|
|
3
|
+
const example = {
|
|
4
|
+
"id": "f485f1ba-de07-422c-8c4b-c2da057d4a44",
|
|
5
|
+
"uid": "f485f1ba-de07-422c-8c4b-c2da057d4a44",
|
|
6
|
+
"is_dir": true,
|
|
7
|
+
"immutable": true,
|
|
8
|
+
"name": "FromParentWindow",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export class FSRelayService extends putility.concepts.Service {
|
|
12
|
+
async _init () {
|
|
13
|
+
const services = this._.context.services;
|
|
14
|
+
const util = this._.context.util;
|
|
15
|
+
const svc_xdIncoming = services.get('xd-incoming');
|
|
16
|
+
svc_xdIncoming.register_tagged_listener('puter-fs', event => {
|
|
17
|
+
util.rpc.send(event.source, event.data.$callback, [example]);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import putility from "@heyputer/putility";
|
|
2
|
+
import { PuterAPIFilesystem } from "../lib/filesystem/APIFS.js";
|
|
3
|
+
import { CachedFilesystem } from "../lib/filesystem/CacheFS.js";
|
|
4
|
+
import { ProxyFilesystem, TFilesystem } from "../lib/filesystem/definitions.js";
|
|
5
|
+
import io from '../lib/socket.io/socket.io.esm.min.js';
|
|
6
|
+
import { PostMessageFilesystem } from "../lib/filesystem/PostMessageFS.js";
|
|
7
|
+
|
|
8
|
+
export class FilesystemService extends putility.concepts.Service {
|
|
9
|
+
static PROPERTIES = {
|
|
10
|
+
// filesystem:
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
static DEPENDS = ['api-access'];
|
|
14
|
+
static HOOKS = [
|
|
15
|
+
{
|
|
16
|
+
service: 'api-access',
|
|
17
|
+
event: 'update',
|
|
18
|
+
description: `
|
|
19
|
+
re-initialize the socket connection whenever the
|
|
20
|
+
authentication token or API origin is changed.
|
|
21
|
+
`,
|
|
22
|
+
async do () {
|
|
23
|
+
this.initializeSocket();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
_init () {
|
|
29
|
+
const env = this._.context.env;
|
|
30
|
+
|
|
31
|
+
if ( env === 'app' ) {
|
|
32
|
+
// TODO: uncomment when relay is ready
|
|
33
|
+
// this.init_app_fs_();
|
|
34
|
+
|
|
35
|
+
this.init_top_fs_();
|
|
36
|
+
} else {
|
|
37
|
+
this.init_top_fs_();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.initializeSocket();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
init_app_fs_ () {
|
|
44
|
+
this.fs_nocache_ = new PostMessageFilesystem({
|
|
45
|
+
messageTarget: globalThis.parent,
|
|
46
|
+
rpc: this._.context.util.rpc,
|
|
47
|
+
}).as(TFilesystem);
|
|
48
|
+
this.filesystem = this.fs_nocache_;
|
|
49
|
+
}
|
|
50
|
+
init_top_fs_ () {
|
|
51
|
+
const api_info = this._.context.services.get('api-access').get_api_info();
|
|
52
|
+
this.fs_nocache_ = new PuterAPIFilesystem({ api_info }).as(TFilesystem);
|
|
53
|
+
this.fs_cache_ = new CachedFilesystem({ delegate: this.fs_nocache_ }).as(TFilesystem);
|
|
54
|
+
// this.filesystem = this.fs_nocache;
|
|
55
|
+
this.fs_proxy_ = new ProxyFilesystem({ delegate: this.fs_nocache_ });
|
|
56
|
+
this.filesystem = this.fs_proxy_.as(TFilesystem);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
cache_on () {
|
|
60
|
+
this.fs_proxy_.delegate = this.fs_cache_;
|
|
61
|
+
}
|
|
62
|
+
cache_off () {
|
|
63
|
+
this.fs_proxy_.delegate = this.fs_nocache_;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async initializeSocket () {
|
|
67
|
+
if (this.socket) {
|
|
68
|
+
this.socket.disconnect();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const svc_apiAccess = this._.context.services.get('api-access');
|
|
72
|
+
const api_info = svc_apiAccess.get_api_info();
|
|
73
|
+
|
|
74
|
+
if ( api_info.api_origin === undefined ) {
|
|
75
|
+
// This will get called again later with updated information
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.socket = io(api_info.api_origin, {
|
|
80
|
+
auth: { auth_token: api_info.auth_token }
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
this.bindSocketEvents();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
bindSocketEvents() {
|
|
87
|
+
this.socket.on('connect', () => {
|
|
88
|
+
if(puter.debugMode)
|
|
89
|
+
console.log('FileSystem Socket: Connected', this.socket.id);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.socket.on('disconnect', () => {
|
|
93
|
+
if(puter.debugMode)
|
|
94
|
+
console.log('FileSystem Socket: Disconnected');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this.socket.on('reconnect', (attempt) => {
|
|
98
|
+
if(puter.debugMode)
|
|
99
|
+
console.log('FileSystem Socket: Reconnected', this.socket.id);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
this.socket.on('reconnect_attempt', (attempt) => {
|
|
103
|
+
if(puter.debugMode)
|
|
104
|
+
console.log('FileSystem Socket: Reconnection Attemps', attempt);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
this.socket.on('reconnect_error', (error) => {
|
|
108
|
+
if(puter.debugMode)
|
|
109
|
+
console.log('FileSystem Socket: Reconnection Error', error);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
this.socket.on('reconnect_failed', () => {
|
|
113
|
+
if(puter.debugMode)
|
|
114
|
+
console.log('FileSystem Socket: Reconnection Failed');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this.socket.on('error', (error) => {
|
|
118
|
+
if(puter.debugMode)
|
|
119
|
+
console.error('FileSystem Socket Error:', error);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import putility from "@heyputer/putility";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Runs commands on the special `globalThis.when_puter_happens` global, for
|
|
5
|
+
* situations where the `puter` global doesn't exist soon enough.
|
|
6
|
+
*/
|
|
7
|
+
export class NoPuterYetService extends putility.concepts.Service {
|
|
8
|
+
_init () {
|
|
9
|
+
if ( ! globalThis.when_puter_happens ) return;
|
|
10
|
+
if ( puter && puter.env !== 'gui' ) return;
|
|
11
|
+
|
|
12
|
+
if ( ! Array.isArray(globalThis.when_puter_happens) ) {
|
|
13
|
+
globalThis.when_puter_happens = [globalThis.when_puter_happens];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
for ( const fn of globalThis.when_puter_happens ) {
|
|
17
|
+
fn({ context: this._.context });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import putility from "@heyputer/putility";
|
|
2
|
+
|
|
3
|
+
const TeePromise = putility.libs.promise.TeePromise;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Manages message events from the window object.
|
|
7
|
+
*/
|
|
8
|
+
export class XDIncomingService extends putility.concepts.Service {
|
|
9
|
+
_construct () {
|
|
10
|
+
this.filter_listeners_ = [];
|
|
11
|
+
this.tagged_listeners_ = {};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
_init () {
|
|
15
|
+
globalThis.addEventListener('message', async event => {
|
|
16
|
+
for ( const fn of this.filter_listeners_ ) {
|
|
17
|
+
const tp = new TeePromise();
|
|
18
|
+
fn(event, tp);
|
|
19
|
+
if ( await tp ) return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const data = event.data;
|
|
23
|
+
|
|
24
|
+
const tag = data.$;
|
|
25
|
+
if ( ! tag ) return;
|
|
26
|
+
if ( ! this.tagged_listeners_[tag] ) return;
|
|
27
|
+
|
|
28
|
+
for ( const fn of this.tagged_listeners_[tag] ) {
|
|
29
|
+
fn({ data, source: event.source });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
register_filter_listener (fn) {
|
|
35
|
+
this.filter_listeners_.push(fn);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
register_tagged_listener (tag, fn) {
|
|
39
|
+
if ( ! this.tagged_listeners_[tag] ) {
|
|
40
|
+
this.tagged_listeners_[tag] = [];
|
|
41
|
+
}
|
|
42
|
+
this.tagged_listeners_[tag].push(fn);
|
|
43
|
+
}
|
|
44
|
+
}
|