@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,110 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024-present Puter Technologies Inc.
|
|
3
|
+
*
|
|
4
|
+
* This file is part of Puter.
|
|
5
|
+
*
|
|
6
|
+
* Puter is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
8
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* This program is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU Affero General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* APICallLogger provides centralized logging for all API calls made by the puter-js SDK.
|
|
23
|
+
* It logs API calls in a simple format: service - operation - params - result
|
|
24
|
+
*/
|
|
25
|
+
class APICallLogger {
|
|
26
|
+
constructor(config = {}) {
|
|
27
|
+
this.config = {
|
|
28
|
+
enabled: config.enabled ?? false,
|
|
29
|
+
...config
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Updates the logger configuration
|
|
35
|
+
* @param {Object} newConfig - New configuration options
|
|
36
|
+
*/
|
|
37
|
+
updateConfig(newConfig) {
|
|
38
|
+
this.config = { ...this.config, ...newConfig };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Enables API call logging
|
|
43
|
+
*/
|
|
44
|
+
enable() {
|
|
45
|
+
this.config.enabled = true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Disables API call logging
|
|
50
|
+
*/
|
|
51
|
+
disable() {
|
|
52
|
+
this.config.enabled = false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Checks if logging is enabled for the current configuration
|
|
57
|
+
* @returns {boolean}
|
|
58
|
+
*/
|
|
59
|
+
isEnabled() {
|
|
60
|
+
return this.config.enabled;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Logs the completion of an API request in a simple format
|
|
65
|
+
* @param {Object} options - Request completion options
|
|
66
|
+
*/
|
|
67
|
+
logRequest(options = {}) {
|
|
68
|
+
if (!this.isEnabled()) return;
|
|
69
|
+
|
|
70
|
+
const {
|
|
71
|
+
service = 'unknown',
|
|
72
|
+
operation = 'unknown',
|
|
73
|
+
params = {},
|
|
74
|
+
result = null,
|
|
75
|
+
error = null
|
|
76
|
+
} = options;
|
|
77
|
+
|
|
78
|
+
// Format params as a readable string
|
|
79
|
+
let paramsStr = '{}';
|
|
80
|
+
if (params && Object.keys(params).length > 0) {
|
|
81
|
+
try {
|
|
82
|
+
paramsStr = JSON.stringify(params);
|
|
83
|
+
} catch (e) {
|
|
84
|
+
paramsStr = '[Unable to serialize params]';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Format the log message with bold params
|
|
89
|
+
const logMessage = `${service} - ${operation} - \x1b[1m${paramsStr}\x1b[22m`;
|
|
90
|
+
|
|
91
|
+
if (error) {
|
|
92
|
+
console.error(logMessage, { error: error.message || error, result });
|
|
93
|
+
} else {
|
|
94
|
+
console.log(logMessage, result);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Gets current logging statistics
|
|
100
|
+
* @returns {Object}
|
|
101
|
+
*/
|
|
102
|
+
getStats() {
|
|
103
|
+
return {
|
|
104
|
+
enabled: this.config.enabled,
|
|
105
|
+
config: { ...this.config }
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export default APICallLogger;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export default class EventListener {
|
|
2
|
+
// Array of all supported event names.
|
|
3
|
+
#eventNames;
|
|
4
|
+
|
|
5
|
+
// Map of eventName -> array of listeners
|
|
6
|
+
#eventListeners;
|
|
7
|
+
|
|
8
|
+
constructor(eventNames) {
|
|
9
|
+
this.#eventNames = eventNames;
|
|
10
|
+
|
|
11
|
+
this.#eventListeners = (() => {
|
|
12
|
+
const map = new Map();
|
|
13
|
+
for (let eventName of this.#eventNames) {
|
|
14
|
+
map[eventName] = [];
|
|
15
|
+
}
|
|
16
|
+
return map;
|
|
17
|
+
})();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
emit(eventName, data) {
|
|
21
|
+
if (!this.#eventNames.includes(eventName)) {
|
|
22
|
+
console.error(`Event name '${eventName}' not supported`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
this.#eventListeners[eventName].forEach((listener) => {
|
|
26
|
+
listener(data);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
on(eventName, callback) {
|
|
31
|
+
if (!this.#eventNames.includes(eventName)) {
|
|
32
|
+
console.error(`Event name '${eventName}' not supported`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.#eventListeners[eventName].push(callback);
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
off(eventName, callback) {
|
|
40
|
+
if (!this.#eventNames.includes(eventName)) {
|
|
41
|
+
console.error(`Event name '${eventName}' not supported`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const listeners = this.#eventListeners[eventName];
|
|
45
|
+
const index = listeners.indexOf(callback)
|
|
46
|
+
if (index !== -1) {
|
|
47
|
+
listeners.splice(index, 1);
|
|
48
|
+
}
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as utils from '../utils.js';
|
|
2
|
+
import putility from "@heyputer/putility";
|
|
3
|
+
import { TeePromise } from "@heyputer/putility/src/libs/promise.js";
|
|
4
|
+
import getAbsolutePathForApp from '../../modules/FileSystem/utils/getAbsolutePathForApp.js';
|
|
5
|
+
import { TFilesystem } from './definitions.js';
|
|
6
|
+
|
|
7
|
+
export class PuterAPIFilesystem extends putility.AdvancedBase {
|
|
8
|
+
constructor ({ api_info }) {
|
|
9
|
+
super();
|
|
10
|
+
this.api_info = api_info;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static IMPLEMENTS = {
|
|
14
|
+
[TFilesystem]: {
|
|
15
|
+
stat: async function (options) {
|
|
16
|
+
this.ensure_auth_();
|
|
17
|
+
const tp = new TeePromise();
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const xhr = new utils.initXhr('/stat', this.api_info.APIOrigin, undefined, "post", "text/plain;actually=json");
|
|
21
|
+
utils.setupXhrEventHandlers(xhr, undefined, undefined,
|
|
22
|
+
tp.resolve.bind(tp),
|
|
23
|
+
tp.reject.bind(tp),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
let dataToSend = {};
|
|
27
|
+
if (options.uid !== undefined) {
|
|
28
|
+
dataToSend.uid = options.uid;
|
|
29
|
+
} else if (options.path !== undefined) {
|
|
30
|
+
// If dirPath is not provided or it's not starting with a slash, it means it's a relative path
|
|
31
|
+
// in that case, we need to prepend the app's root directory to it
|
|
32
|
+
dataToSend.path = getAbsolutePathForApp(options.path);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
dataToSend.return_subdomains = options.returnSubdomains;
|
|
36
|
+
dataToSend.return_permissions = options.returnPermissions;
|
|
37
|
+
dataToSend.return_versions = options.returnVersions;
|
|
38
|
+
dataToSend.return_size = options.returnSize;
|
|
39
|
+
dataToSend.auth_token = this.api_info.authToken;
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
xhr.send(JSON.stringify(dataToSend));
|
|
43
|
+
|
|
44
|
+
return await tp;
|
|
45
|
+
},
|
|
46
|
+
readdir: async function (options) {
|
|
47
|
+
this.ensure_auth_();
|
|
48
|
+
const tp = new TeePromise();
|
|
49
|
+
|
|
50
|
+
const xhr = new utils.initXhr('/readdir', this.api_info.APIOrigin, undefined, "post", "text/plain;actually=json");
|
|
51
|
+
utils.setupXhrEventHandlers(xhr, undefined, undefined,
|
|
52
|
+
tp.resolve.bind(tp),
|
|
53
|
+
tp.reject.bind(tp),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
xhr.send(JSON.stringify({ path: getAbsolutePathForApp(options.path), auth_token: this.api_info.authToken }));
|
|
57
|
+
|
|
58
|
+
return await tp;
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
ensure_auth_ () {
|
|
64
|
+
// TODO: remove reference to global 'puter'; get 'env' via context
|
|
65
|
+
if ( ! this.api_info.authToken && puter.env === 'web' ) {
|
|
66
|
+
try {
|
|
67
|
+
this.ui.authenticateWithPuter();
|
|
68
|
+
} catch (e) {
|
|
69
|
+
throw new Error('Authentication failed.');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import putility from "@heyputer/putility";
|
|
2
|
+
import { RWLock } from "@heyputer/putility/src/libs/promise.js";
|
|
3
|
+
import { ProxyFilesystem, TFilesystem } from "./definitions.js";
|
|
4
|
+
import { uuidv4 } from "../utils.js";
|
|
5
|
+
|
|
6
|
+
export const ROOT_UUID = '00000000-0000-0000-0000-000000000000';
|
|
7
|
+
const TTL = 5 * 1000;
|
|
8
|
+
|
|
9
|
+
export class CacheFS extends putility.AdvancedBase {
|
|
10
|
+
static PROPERTIES = {
|
|
11
|
+
assocs_path_: () => ({}),
|
|
12
|
+
assocs_uuid_: () => ({}),
|
|
13
|
+
entries: () => ({}),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
get_entry_ei (external_identifier) {
|
|
17
|
+
if ( Array.isArray(external_identifier) ) {
|
|
18
|
+
for ( const ei of external_identifier ) {
|
|
19
|
+
const entry = this.get_entry_ei(ei);
|
|
20
|
+
if ( entry ) return entry;
|
|
21
|
+
}
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log('GET ENTRY EI', external_identifier);
|
|
26
|
+
|
|
27
|
+
const internal_identifier =
|
|
28
|
+
this.assocs_path_[external_identifier] ||
|
|
29
|
+
this.assocs_uuid_[external_identifier] ||
|
|
30
|
+
external_identifier;
|
|
31
|
+
|
|
32
|
+
if ( ! internal_identifier ) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
return this.entries[internal_identifier];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
add_entry ({ id } = {}) {
|
|
39
|
+
const internal_identifier = id ?? uuidv4();
|
|
40
|
+
const entry = {
|
|
41
|
+
id: internal_identifier,
|
|
42
|
+
stat_has: {},
|
|
43
|
+
stat_exp: 0,
|
|
44
|
+
locks: {
|
|
45
|
+
stat: new RWLock(),
|
|
46
|
+
members: new RWLock(),
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
this.entries[internal_identifier] = entry;
|
|
50
|
+
return entry;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
assoc_path (path, internal_identifier) {
|
|
54
|
+
console.log('ASSOC PATH', path, internal_identifier);
|
|
55
|
+
this.assocs_path_[path] = internal_identifier;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
assoc_uuid (uuid, internal_identifier) {
|
|
59
|
+
if ( uuid === internal_identifier ) return;
|
|
60
|
+
this.assocs_uuid_[uuid] = internal_identifier;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export class CachedFilesystem extends ProxyFilesystem {
|
|
66
|
+
constructor (o) {
|
|
67
|
+
super(o);
|
|
68
|
+
// this.cacheFS = cacheFS;
|
|
69
|
+
this.cacheFS = new CacheFS();
|
|
70
|
+
}
|
|
71
|
+
static IMPLEMENTS = {
|
|
72
|
+
[TFilesystem]: {
|
|
73
|
+
stat: async function (o) {
|
|
74
|
+
let cent = this.cacheFS.get_entry_ei(o.path ?? o.uid);
|
|
75
|
+
|
|
76
|
+
const modifiers = [
|
|
77
|
+
'subdomains',
|
|
78
|
+
'permissions',
|
|
79
|
+
'versions',
|
|
80
|
+
'size',
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
let values_requested = {};
|
|
84
|
+
for ( const mod of modifiers ) {
|
|
85
|
+
const optionsKey = 'return' +
|
|
86
|
+
mod.charAt(0).toUpperCase() +
|
|
87
|
+
mod.slice(1);
|
|
88
|
+
if ( ! o[optionsKey] ) continue;
|
|
89
|
+
values_requested[mod] = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const satisfactory_cache = cent => {
|
|
93
|
+
for ( const mod of modifiers ) {
|
|
94
|
+
if ( ! values_requested[mod] ) continue;
|
|
95
|
+
if ( ! cent.stat_has[mod] ) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let cached_stat;
|
|
103
|
+
if ( cent && cent.stat && cent.stat_exp > Date.now() ) {
|
|
104
|
+
const l = await cent.locks.stat.rlock();
|
|
105
|
+
if ( satisfactory_cache(cent) ) {
|
|
106
|
+
cached_stat = cent.stat;
|
|
107
|
+
}
|
|
108
|
+
l.unlock();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if ( cached_stat ) {
|
|
112
|
+
console.log('CACHE HIT');
|
|
113
|
+
return cached_stat;
|
|
114
|
+
}
|
|
115
|
+
console.log('CACHE MISS');
|
|
116
|
+
|
|
117
|
+
let l;
|
|
118
|
+
if ( cent ) {
|
|
119
|
+
l = await cent.locks.stat.wlock();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log('DOING THE STAT', o);
|
|
123
|
+
const entry = await this.delegate.stat(o);
|
|
124
|
+
|
|
125
|
+
// We might have new information to identify a relevant cache entry
|
|
126
|
+
let cent_replaced = !! cent;
|
|
127
|
+
cent = this.cacheFS.get_entry_ei([entry.uid, entry.path]);
|
|
128
|
+
if ( cent ) {
|
|
129
|
+
if ( cent_replaced ) l.unlock();
|
|
130
|
+
l = await cent.locks.stat.wlock();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if ( ! cent ) {
|
|
134
|
+
cent = this.cacheFS.add_entry({ id: entry.uid });
|
|
135
|
+
this.cacheFS.assoc_path(entry.path, cent.id);
|
|
136
|
+
this.cacheFS.assoc_uuid(entry.uid, cent.id);
|
|
137
|
+
|
|
138
|
+
l = await cent.locks.stat.wlock();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
cent.stat = entry;
|
|
142
|
+
cent.stat_has = { ...values_requested };
|
|
143
|
+
// TODO: increase cache TTL once invalidation works
|
|
144
|
+
cent.stat_exp = Date.now() + TTL;
|
|
145
|
+
|
|
146
|
+
l.unlock();
|
|
147
|
+
|
|
148
|
+
console.log('RETRUNING THE ENTRY', entry);
|
|
149
|
+
return entry;
|
|
150
|
+
},
|
|
151
|
+
readdir: async function (o) {
|
|
152
|
+
let cent = this.cacheFS.get_entry_ei([o.path, o.uid]);
|
|
153
|
+
|
|
154
|
+
console.log('CENT', cent, o);
|
|
155
|
+
let stats = null;
|
|
156
|
+
if ( cent && cent.members && cent.members_exp > Date.now() ) {
|
|
157
|
+
console.log('MEMBERS', cent.members);
|
|
158
|
+
stats = [];
|
|
159
|
+
const l = await cent.locks.stat.rlock();
|
|
160
|
+
|
|
161
|
+
for ( const id of cent.members ) {
|
|
162
|
+
const member = this.cacheFS.get_entry_ei(id);
|
|
163
|
+
if ( ! member || ! member.stat || member.stat_exp <= Date.now() ) {
|
|
164
|
+
console.log('NO MEMBER OR STAT', member);
|
|
165
|
+
stats = null;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
console.log('member', member);
|
|
169
|
+
if ( ! o.no_assocs && ! member.stat_has.subdomains ) {
|
|
170
|
+
stats = null;
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
if ( ! o.no_assocs && ! member.stat_has.apps ) {
|
|
174
|
+
stats = null;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
if ( ! o.no_thumbs && ! member.stat_has.thumbnail ) {
|
|
178
|
+
stats = null;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
console.log('PUSHING', member.stat);
|
|
182
|
+
|
|
183
|
+
stats.push(member.stat);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
l.unlock();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log('STATS????', stats);
|
|
190
|
+
if ( stats ) {
|
|
191
|
+
return stats;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let l;
|
|
195
|
+
if ( cent ) {
|
|
196
|
+
l = await cent.locks.members.wlock();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const entries = await this.delegate.readdir(o);
|
|
200
|
+
if ( ! cent ) {
|
|
201
|
+
cent = this.cacheFS.add_entry(o.uid ? { id: o.uid } : {});
|
|
202
|
+
if ( o.path ) this.cacheFS.assoc_path(o.path, cent.id);
|
|
203
|
+
l = await cent.locks.members.wlock();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let cent_ids = [];
|
|
207
|
+
for ( const entry of entries ) {
|
|
208
|
+
let entry_cent = this.cacheFS.get_entry_ei([entry.path, entry.uid]);
|
|
209
|
+
if ( ! entry_cent ) {
|
|
210
|
+
entry_cent = this.cacheFS.add_entry({ id: entry.uid });
|
|
211
|
+
this.cacheFS.assoc_path(entry.path, entry.uid);
|
|
212
|
+
}
|
|
213
|
+
cent_ids.push(entry_cent.id);
|
|
214
|
+
// TODO: update_stat_ is not implemented
|
|
215
|
+
// this.cacheFS.update_stat_(entry_cent, entry, {
|
|
216
|
+
// subdomains: ! o.no_assocs,
|
|
217
|
+
// apps: ! o.no_assocs,
|
|
218
|
+
// thumbnail: ! o.no_thumbs,
|
|
219
|
+
// });
|
|
220
|
+
entry_cent.stat = entry;
|
|
221
|
+
entry_cent.stat_has = {
|
|
222
|
+
subdomains: ! o.no_assocs,
|
|
223
|
+
apps: ! o.no_assocs,
|
|
224
|
+
thumbnail: ! o.no_thumbs,
|
|
225
|
+
}
|
|
226
|
+
entry_cent.stat_exp = Date.now() + 1000*3;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
cent.members = []
|
|
230
|
+
for ( const id of cent_ids ) {
|
|
231
|
+
cent.members.push(id);
|
|
232
|
+
}
|
|
233
|
+
cent.members_exp = Date.now() + TTL;
|
|
234
|
+
|
|
235
|
+
l.unlock();
|
|
236
|
+
|
|
237
|
+
console.log('CACHE ENTRY?', cent);
|
|
238
|
+
|
|
239
|
+
return entries;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import putility from "@heyputer/putility";
|
|
2
|
+
import { TFilesystem } from "./definitions.js";
|
|
3
|
+
|
|
4
|
+
const example = {
|
|
5
|
+
"id": "f485f1ba-de07-422c-8c4b-c2da057d4a44",
|
|
6
|
+
"uid": "f485f1ba-de07-422c-8c4b-c2da057d4a44",
|
|
7
|
+
"is_dir": true,
|
|
8
|
+
"immutable": true,
|
|
9
|
+
"name": "Test",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export class PostMessageFilesystem extends putility.AdvancedBase {
|
|
13
|
+
constructor ({ rpc, messageTarget }) {
|
|
14
|
+
super();
|
|
15
|
+
this.rpc = rpc;
|
|
16
|
+
this.messageTarget = messageTarget;
|
|
17
|
+
}
|
|
18
|
+
static IMPLEMENTS = {
|
|
19
|
+
[TFilesystem]: {
|
|
20
|
+
stat: async function (o) {
|
|
21
|
+
return example;
|
|
22
|
+
},
|
|
23
|
+
readdir: async function (o) {
|
|
24
|
+
const tp = new putility.libs.promise.TeePromise();
|
|
25
|
+
const $callback = this.rpc.registerCallback((result) => {
|
|
26
|
+
tp.resolve(result);
|
|
27
|
+
});
|
|
28
|
+
// return [example];
|
|
29
|
+
this.messageTarget.postMessage({
|
|
30
|
+
$: 'puter-fs',
|
|
31
|
+
$callback,
|
|
32
|
+
op: 'readdir',
|
|
33
|
+
args: o,
|
|
34
|
+
}, '*');
|
|
35
|
+
|
|
36
|
+
return await tp;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import putility from "@heyputer/putility";
|
|
2
|
+
|
|
3
|
+
export const TFilesystem = 'TFilesystem';
|
|
4
|
+
|
|
5
|
+
// TODO: UNUSED (eventually putility will support these definitions)
|
|
6
|
+
// This is here so that the idea is not forgotten.
|
|
7
|
+
export const IFilesystem = {
|
|
8
|
+
methods: {
|
|
9
|
+
stat: {
|
|
10
|
+
parameters: {
|
|
11
|
+
path: {
|
|
12
|
+
alias: 'uid',
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export class ProxyFilesystem extends putility.AdvancedBase {
|
|
21
|
+
static PROPERTIES = {
|
|
22
|
+
delegate: () => {},
|
|
23
|
+
}
|
|
24
|
+
// TODO: constructor implied by properties
|
|
25
|
+
constructor ({ delegate }) {
|
|
26
|
+
super();
|
|
27
|
+
this.delegate = delegate;
|
|
28
|
+
}
|
|
29
|
+
static IMPLEMENTS = {
|
|
30
|
+
[TFilesystem]: {
|
|
31
|
+
stat: async function (o) {
|
|
32
|
+
return this.delegate.stat(o);
|
|
33
|
+
},
|
|
34
|
+
readdir: async function (o) {
|
|
35
|
+
return this.delegate.readdir(o);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|