refactor: flatten the monorepo

This commit is contained in:
Eric Dubé 2024-06-08 01:07:42 -04:00 committed by GitHub
parent f88c4a5c9c
commit fa7bec3854
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 218 additions and 340 deletions

30
package-lock.json generated
View File

@ -7,12 +7,12 @@
"": {
"name": "puter.com",
"version": "2.3.0",
"hasInstallScript": true,
"license": "AGPL-3.0-only",
"workspaces": [
"packages/*"
],
"dependencies": {
"@xterm/xterm": "^5.5.0",
"json-colorizer": "^3.0.1",
"uuid": "^9.0.1"
},
@ -5456,6 +5456,10 @@
"node": ">= 0.6"
}
},
"node_modules/contextlink": {
"resolved": "packages/contextlink",
"link": true
},
"node_modules/convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
@ -5789,6 +5793,10 @@
"node": ">=8"
}
},
"node_modules/dev-pty": {
"resolved": "packages/pty",
"link": true
},
"node_modules/diff": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
@ -10712,6 +10720,10 @@
"node": ">= 0.8"
}
},
"node_modules/strataparse": {
"resolved": "packages/strataparse",
"link": true
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@ -12061,6 +12073,13 @@
"typescript": "^5.1.6"
}
},
"packages/contextlink": {
"version": "0.0.0",
"license": "AGPL-3.0-only",
"devDependencies": {
"mocha": "^10.2.0"
}
},
"packages/git": {
"version": "1.0.0",
"license": "AGPL-3.0-only",
@ -12312,6 +12331,11 @@
"extraneous": true,
"license": "AGPL-3.0-only"
},
"packages/pty": {
"name": "dev-pty",
"version": "0.0.0",
"license": "AGPL-3.0-only"
},
"packages/puter-js": {
"name": "@heyputer/puterjs",
"version": "1.0.0",
@ -12326,6 +12350,10 @@
"version": "1.0.0",
"license": "UNLICENSED"
},
"packages/strataparse": {
"version": "0.0.0",
"license": "AGPL-3.0-only"
},
"packages/terminal": {
"name": "@heyputer/terminal",
"version": "0.0.0",

View File

@ -29,8 +29,7 @@
"start=gui": "nodemon --exec \"node dev-server.js\" ",
"start": "node run-selfhosted.js",
"build": "node ./build.js",
"check-translations": "node tools/check-translations.js",
"postinstall": "cd packages/phoenix && cd packages/contextlink && npm install && cd - && cd packages/strataparse && npm install && cd - && cd packages/pty && npm install"
"check-translations": "node tools/check-translations.js"
},
"workspaces": [
"packages/*"

View File

@ -1,23 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const { AdvancedBase } = require('./src/AdvancedBase');
module.exports = {
AdvancedBase,
};

View File

@ -1,11 +0,0 @@
{
"name": "@heyputer/puter-js-common",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Puter Technologies Inc.",
"license": "UNLICENSED"
}

View File

@ -1,33 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// This doesn't go in ./bases because it logically depends on
// both ./bases and ./traits, and ./traits depends on ./bases.
const { TraitBase } = require("./bases/TraitBase");
class AdvancedBase extends TraitBase {
static TRAITS = [
require('./traits/NodeModuleDITrait'),
require('./traits/PropertiesTrait'),
]
}
module.exports = {
AdvancedBase,
};

View File

@ -1,55 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
class BasicBase {
_get_inheritance_chain () {
const chain = [];
let cls = this.constructor;
while ( cls && cls !== BasicBase ) {
chain.push(cls);
cls = cls.__proto__;
}
return chain.reverse();
}
_get_merged_static_array (key) {
const chain = this._get_inheritance_chain();
const values = [];
for ( const cls of chain ) {
if ( cls[key] ) {
values.push(...cls[key]);
}
}
return values;
}
_get_merged_static_object (key) {
const chain = this._get_inheritance_chain();
const values = {};
for ( const cls of chain ) {
if ( cls[key] ) {
Object.assign(values, cls[key]);
}
}
return values;
}
}
module.exports = {
BasicBase,
};

View File

@ -1,41 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const { BasicBase } = require("./BasicBase");
class TraitBase extends BasicBase {
constructor (parameters, ...a) {
super(parameters, ...a);
for ( const trait of this.traits ) {
trait.install_in_instance(
this,
{
parameters: parameters || {},
}
)
}
}
get traits () {
return this._get_merged_static_array('TRAITS');
}
}
module.exports = {
TraitBase,
};

View File

@ -1,59 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* This trait allows dependency injection of node modules.
* This is incredibly useful for passing mock implementations
* of modules for unit testing.
*
* @example
* class MyClass extends AdvancedBase {
* static MODULES = {
* axios,
* };
* }
*
* const my_class = new MyClass({
* modules: {
* axios: MY_AXIOS_MOCK,
* }
* });
*/
module.exports = {
install_in_instance: (instance, { parameters }) => {
const modules = instance._get_merged_static_object('MODULES');
if ( parameters.modules ) {
for ( const k in parameters.modules ) {
modules[k] = parameters.modules[k];
}
}
instance.modules = modules;
// This "require" function can shadow the real one so
// that editor tools are aware of the modules that
// are being used.
instance.require = (name) => {
if ( modules[name] ) {
return modules[name];
}
return require(name);
}
},
};

View File

@ -1,38 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
module.exports = {
install_in_instance: (instance) => {
const properties = instance._get_merged_static_object('PROPERTIES');
for ( const k in properties ) {
if ( typeof properties[k] === 'function' ) {
instance[k] = properties[k]();
continue;
}
if ( typeof properties[k] === 'object' ) {
// This will be supported in the future.
throw new Error(`Property ${k} in ${instance.constructor.name} ` +
`is not a supported property specification.`);
}
instance[k] = properties[k];
}
}
}

View File

@ -1,72 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const { expect } = require('chai');
const { BasicBase } = require('../src/bases/BasicBase');
const { AdvancedBase } = require('../src/AdvancedBase');
class ClassA extends BasicBase {
static STATIC_OBJ = {
a: 1,
b: 2,
};
static STATIC_ARR = ['a', 'b'];
}
class ClassB extends ClassA {
static STATIC_OBJ = {
c: 3,
d: 4,
};
static STATIC_ARR = ['c', 'd'];
}
describe('testing', () => {
it('does a thing', () => {
const b = new ClassB();
console.log(b._get_inheritance_chain());
console.log([ClassA, ClassB]);
expect(b._get_inheritance_chain()).deep.equal([ClassA, ClassB]);
expect(b._get_merged_static_array('STATIC_ARR'))
.deep.equal(['a', 'b', 'c', 'd']);
expect(b._get_merged_static_object('STATIC_OBJ'))
.deep.equal({ a: 1, b: 2, c: 3, d: 4 });
});
});
class ClassWithModule extends AdvancedBase {
static MODULES = {
axios: 'axios',
};
}
describe('AdvancedBase', () => {
it('passes DI modules to instance', () => {
const c1 = new ClassWithModule();
expect(c1.modules.axios).to.equal('axios');
const c2 = new ClassWithModule({
modules: {
axios: 'my-axios',
},
});
expect(c2.modules.axios).to.equal('my-axios');
});
});

View File

@ -20,6 +20,7 @@ import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs';
import copy from 'rollup-plugin-copy';
import process from 'node:process';
import path from 'node:path';
const configFile = process.env.CONFIG_FILE ?? 'config/dev.js';
await import(`./${configFile}`);
@ -34,6 +35,7 @@ export default {
nodeResolve({
browser: true,
preferBuiltins: false,
rootDir: path.join(process.cwd(), '..'),
}),
commonjs(),
copy({

View File

@ -20,6 +20,7 @@ import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs';
import copy from 'rollup-plugin-copy';
import process from 'node:process';
import path from 'node:path';
const configFile = process.env.CONFIG_FILE ?? 'config/dev.js';
await import(`./${configFile}`);
@ -31,7 +32,9 @@ export default {
format: "iife"
},
plugins: [
nodeResolve(),
nodeResolve({
rootDir: path.join(process.cwd(), '..'),
}),
commonjs(),
copy({
targets: [

View File

@ -16,7 +16,8 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { TeePromise, raceCase } from '../../src/promise.js';
import { libs } from '@heyputer/puter-js-common';
const { TeePromise, raceCase } = libs.promise;
const encoder = new TextEncoder();
@ -187,7 +188,7 @@ export class BetterReader {
}
if ( which === 'cancel' ) {
this.channel_.pushback(...chunks);
return
return
}
if ( n_read + chunk.length > opt_buffer.length ) {
const diff = opt_buffer.length - n_read;
@ -320,7 +321,7 @@ export class PTT {
/**
* PTY: pseudo-terminal
*
*
* This implements the PTY device driver.
*/
export class PTY {

View File

@ -2,4 +2,7 @@ const { AdvancedBase } = require('./src/AdvancedBase');
module.exports = {
AdvancedBase,
libs: {
promise: require('./src/libs/promise'),
},
};

View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
class TeePromise {
static STATUS_PENDING = Symbol('pending');
static STATUS_RUNNING = {};
static STATUS_DONE = Symbol('done');
constructor () {
this.status_ = this.constructor.STATUS_PENDING;
this.donePromise = new Promise((resolve, reject) => {
this.doneResolve = resolve;
this.doneReject = reject;
});
}
get status () {
return this.status_;
}
set status (status) {
this.status_ = status;
if ( status === this.constructor.STATUS_DONE ) {
this.doneResolve();
}
}
resolve (value) {
this.status_ = this.constructor.STATUS_DONE;
this.doneResolve(value);
}
awaitDone () {
return this.donePromise;
}
then (fn, ...a) {
return this.donePromise.then(fn, ...a);
}
reject (err) {
this.status_ = this.constructor.STATUS_DONE;
this.doneReject(err);
}
/**
* @deprecated use then() instead
*/
onComplete(fn) {
return this.then(fn);
}
}
class Lock {
constructor() {
this._locked = false;
this._waiting = [];
}
async acquire(callback) {
await new Promise(resolve => {
if ( ! this._locked ) {
this._locked = true;
resolve();
} else {
this._waiting.push({
resolve,
});
}
})
if ( callback ) {
let retval;
try {
retval = await callback();
} finally {
this.release();
}
return retval;
}
}
release() {
if (this._waiting.length > 0) {
const { resolve } = this._waiting.shift();
resolve();
} else {
this._locked = false;
}
}
}
/**
* @callback behindScheduleCallback
* @param {number} drift - The number of milliseconds that the callback was
* called behind schedule.
* @returns {boolean} - If the callback returns true, the timer will be
* cancelled.
*/
/**
* When passing an async callback to setInterval, it's possible for the
* callback to be called again before the previous invocation has finished.
*
* This function wraps setInterval and ensures that the callback is not
* called again until the previous invocation has finished.
*
* @param {Function} callback - The function to call when the timer elapses.
* @param {number} delay - The minimum number of milliseconds between invocations.
* @param {?Array<any>} args - Additional arguments to pass to setInterval.
* @param {?Object} options - Additional options.
* @param {behindScheduleCallback} options.onBehindSchedule - A callback to call when the callback is called behind schedule.
*/
const asyncSafeSetInterval = async (callback, delay, args, options) => {
args = args ?? [];
options = options ?? {};
const { onBehindSchedule } = options;
const sleep = (ms) => new Promise(rslv => setTimeout(rslv, ms));
for ( ;; ) {
await sleep(delay);
const ts_start = Date.now();
await callback(...args);
const ts_end = Date.now();
const runtime = ts_end - ts_start;
const sleep_time = delay - runtime;
if ( sleep_time < 0 ) {
if ( onBehindSchedule ) {
const cancel = await onBehindSchedule(-sleep_time);
if ( cancel ) {
return;
}
}
} else {
await sleep(sleep_time);
}
}
}
/**
* raceCase is like Promise.race except it takes an object instead of
* an array, and returns the key of the promise that resolves first
* as well as the value that it resolved to.
*
* @param {Object.<string, Promise>} promise_map
*
* @returns {Promise.<[string, any]>}
*/
const raceCase = async (promise_map) => {
return Promise.race(Object.entries(promise_map).map(
([key, promise]) => promise.then(value => [key, value])));
};
module.exports = {
TeePromise,
Lock,
asyncSafeSetInterval,
raceCase,
};

View File

@ -20,6 +20,7 @@ import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs';
import copy from 'rollup-plugin-copy';
import process from 'node:process';
import path from 'node:path';
const configFile = process.env.CONFIG_FILE ?? 'config/dev.js';
await import(`./${configFile}`);
@ -31,7 +32,9 @@ export default {
format: "iife"
},
plugins: [
nodeResolve(),
nodeResolve({
rootDir: path.join(process.cwd(), '..'),
}),
commonjs(),
copy({
targets: [