mirror of
https://github.com/HeyPuter/puter.git
synced 2025-12-21 12:59:52 -06:00
cleanup: remove Library + bloated utils where possible (#2142)
This commit is contained in:
@@ -11,36 +11,4 @@ const config = use('core.config');
|
||||
extension.get('/get-origin', { noauth: true }, (req, res) => {
|
||||
res.send(config.origin);
|
||||
})
|
||||
```
|
||||
|
||||
### `core.util.*` - Utility Functions
|
||||
|
||||
These utilities come from `src/backend/src/util` in Puter's repo.
|
||||
Each file in this directory has its exports auto-loaded into this
|
||||
namespace. For example, `src/backend/src/util/langutil.js` is available
|
||||
via `use('core.util.langutil')` or `use.core.util.langutil`.
|
||||
|
||||
#### `core.util.helpers` - Helper Functions
|
||||
|
||||
Common utility functions used throughout Puter's backend. Use with caution as
|
||||
some of these functions may be deprecated.
|
||||
|
||||
> **note:** the following documentation is incomplete
|
||||
|
||||
#### `core.util.langutil` - Language Helpers
|
||||
|
||||
##### `whatis(thing :any)`
|
||||
|
||||
- Returns `"array"` if `thing` is an array.
|
||||
- Returns `"null"` if `thing` is `null`.
|
||||
- Returns `typeof thing` for any other case.
|
||||
|
||||
##### `nou(value :any)`
|
||||
|
||||
Simply a "null or undefined" check.
|
||||
|
||||
##### `can(value :any, capabilities :Array<string>)`
|
||||
|
||||
Checks if something has the specified capabilities. At the time of
|
||||
writing the only one supported is `iterate`, which will check if
|
||||
`value[Symbol.iterator]` is truthy
|
||||
```
|
||||
@@ -18,7 +18,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
const { AdvancedBase } = require('@heyputer/putility');
|
||||
const Library = require('./definitions/Library');
|
||||
const { NotificationES } = require('./om/entitystorage/NotificationES');
|
||||
const { ProtectedAppES } = require('./om/entitystorage/ProtectedAppES');
|
||||
const { Context } = require('./util/context');
|
||||
@@ -41,7 +40,6 @@ const install = async ({ context, services, app, useapi, modapi }) => {
|
||||
useapi.withuse(() => {
|
||||
def('Service', require('./services/BaseService'));
|
||||
def('Module', AdvancedBase);
|
||||
def('Library', Library);
|
||||
|
||||
def('core.util.helpers', require('./helpers'));
|
||||
def('core.util.permission', require('./services/auth/permissionUtils.mjs').PermissionUtil);
|
||||
@@ -88,22 +86,11 @@ const install = async ({ context, services, app, useapi, modapi }) => {
|
||||
runtimeModule.exports = useapi.use('core');
|
||||
});
|
||||
|
||||
useapi.withuse(() => {
|
||||
const ArrayUtil = require('./libraries/ArrayUtil');
|
||||
services.registerService('util-array', ArrayUtil);
|
||||
|
||||
const LibTypeTagged = require('./libraries/LibTypeTagged');
|
||||
services.registerService('lib-type-tagged', LibTypeTagged);
|
||||
});
|
||||
|
||||
modapi.libdir('core.util', './util');
|
||||
|
||||
// === SERVICES ===
|
||||
|
||||
// /!\ IMPORTANT /!\
|
||||
// For new services, put the import immediately above the
|
||||
// call to services.registerService. We'll clean this up
|
||||
// in a future PR.
|
||||
// TODO: move these to top level imports or await imports and esm this file
|
||||
|
||||
const { CommandService } = require('./services/CommandService');
|
||||
const { HTTPThumbnailService } = require('./services/thumbnails/HTTPThumbnailService');
|
||||
|
||||
@@ -40,20 +40,6 @@ class Extension extends AdvancedBase {
|
||||
}),
|
||||
];
|
||||
|
||||
randomBrightColor () {
|
||||
// Bright colors in ANSI (foreground codes 90–97)
|
||||
const brightColors = [
|
||||
// 91, // Bright Red
|
||||
92, // Bright Green
|
||||
// 93, // Bright Yellow
|
||||
94, // Bright Blue
|
||||
95, // Bright Magenta
|
||||
// 96, // Bright Cyan
|
||||
];
|
||||
|
||||
return brightColors[Math.floor(Math.random() * brightColors.length)];
|
||||
}
|
||||
|
||||
constructor (...a) {
|
||||
super(...a);
|
||||
this.service = null;
|
||||
@@ -97,6 +83,20 @@ class Extension extends AdvancedBase {
|
||||
};
|
||||
}
|
||||
|
||||
randomBrightColor () {
|
||||
// Bright colors in ANSI (foreground codes 90–97)
|
||||
const brightColors = [
|
||||
// 91, // Bright Red
|
||||
92, // Bright Green
|
||||
// 93, // Bright Yellow
|
||||
94, // Bright Blue
|
||||
95, // Bright Magenta
|
||||
// 96, // Bright Cyan
|
||||
];
|
||||
|
||||
return brightColors[Math.floor(Math.random() * brightColors.length)];
|
||||
}
|
||||
|
||||
example () {
|
||||
console.log('Example method called by an extension.');
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present 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 BaseService = require('../services/BaseService');
|
||||
|
||||
class Library extends BaseService {
|
||||
//
|
||||
}
|
||||
|
||||
module.exports = Library;
|
||||
@@ -24,7 +24,7 @@ module.exports = function SimpleEntity ({ name, methods, fetchers }) {
|
||||
Object.assign(entity, methods);
|
||||
for ( const fetcher_name in fetchers ) {
|
||||
entity[`fetch_${ fetcher_name}`] = async function () {
|
||||
if ( this.values.hasOwnProperty(fetcher_name) ) {
|
||||
if ( Object.prototype.hasOwnProperty.call(this.values, fetcher_name) ) {
|
||||
return this.values[fetcher_name];
|
||||
}
|
||||
const value = await fetchers[fetcher_name].call(this);
|
||||
|
||||
@@ -47,11 +47,14 @@ const { MANAGE_PERM_PREFIX } = require('../services/auth/permissionConts.mjs');
|
||||
* @property {string} path the path to the filesystem entry
|
||||
* @property {string} uid the UUID of the filesystem entry
|
||||
*/
|
||||
|
||||
const TYPE_FILE = { label: 'File' };
|
||||
const TYPE_DIRECTORY = { label: 'Directory' };
|
||||
module.exports = class FSNodeContext {
|
||||
static CONCERN = 'filesystem';
|
||||
|
||||
static TYPE_FILE = { label: 'File' };
|
||||
static TYPE_DIRECTORY = { label: 'Directory' };
|
||||
static TYPE_FILE = TYPE_FILE;
|
||||
static TYPE_DIRECTORY = TYPE_DIRECTORY;
|
||||
static TYPE_SYMLINK = {};
|
||||
static TYPE_SHORTCUT = {};
|
||||
static TYPE_UNDETERMINED = {};
|
||||
@@ -946,3 +949,6 @@ module.exports = class FSNodeContext {
|
||||
return fsentry;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.TYPE_FILE = TYPE_FILE;
|
||||
module.exports.TYPE_DIRECTORY = TYPE_DIRECTORY;
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
const BaseService = require("../services/BaseService");
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024-present 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 ArrayUtil extends (globalThis.use?.Library ?? BaseService) {
|
||||
/**
|
||||
*
|
||||
* @param {*} marked_map
|
||||
* @param {*} subject
|
||||
*/
|
||||
remove_marked_items (marked_map, subject) {
|
||||
for ( let i = 0 ; i < marked_map.length ; i++ ) {
|
||||
let ii = marked_map[i];
|
||||
// track: type check
|
||||
if ( ! Number.isInteger(ii) ) {
|
||||
throw new Error('marked_map can only contain integers');
|
||||
}
|
||||
// track: bounds check
|
||||
if ( ii < 0 && ii >= subject.length ) {
|
||||
throw new Error('each item in `marked_map` must be within that bounds ' +
|
||||
'of `subject`');
|
||||
}
|
||||
}
|
||||
|
||||
marked_map.sort((a, b) => b - a);
|
||||
|
||||
for ( let i = 0 ; i < marked_map.length ; i++ ) {
|
||||
let ii = marked_map[i];
|
||||
subject.splice(ii, 1);
|
||||
}
|
||||
|
||||
return subject;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ArrayUtil;
|
||||
@@ -1,56 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createTestKernel } from '../../tools/test.mjs';
|
||||
import ArrayUtil from './ArrayUtil.js';
|
||||
|
||||
describe('ArrayUtil', () => {
|
||||
it('should remove marked items correctly', async () => {
|
||||
const testKernel = await createTestKernel({
|
||||
serviceMap: {
|
||||
arrayUtil: ArrayUtil,
|
||||
},
|
||||
});
|
||||
|
||||
const arrayUtil = testKernel.services?.get('arrayUtil');
|
||||
|
||||
// inner indices
|
||||
{
|
||||
const subject = [
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||
];
|
||||
// 0 1 2 3 4 5 6 7
|
||||
const marked_map = [2, 5];
|
||||
arrayUtil.remove_marked_items(marked_map, subject);
|
||||
expect(subject.join('')).toBe('abdegh');
|
||||
}
|
||||
// left edge
|
||||
{
|
||||
const subject = [
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||
];
|
||||
// 0 1 2 3 4 5 6 7
|
||||
const marked_map = [0];
|
||||
arrayUtil.remove_marked_items(marked_map, subject);
|
||||
expect(subject.join('')).toBe('bcdefgh');
|
||||
}
|
||||
// right edge
|
||||
{
|
||||
const subject = [
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||
];
|
||||
// 0 1 2 3 4 5 6 7
|
||||
const marked_map = [7];
|
||||
arrayUtil.remove_marked_items(marked_map, subject);
|
||||
expect(subject.join('')).toBe('abcdefg');
|
||||
}
|
||||
// both edges
|
||||
{
|
||||
const subject = [
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||
];
|
||||
// 0 1 2 3 4 5 6 7
|
||||
const marked_map = [0, 7];
|
||||
arrayUtil.remove_marked_items(marked_map, subject);
|
||||
expect(subject.join('')).toBe('bcdefg');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present 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 { whatis } = require('../util/langutil');
|
||||
|
||||
class LibTypeTagged extends use.Library {
|
||||
process (o) {
|
||||
const could_be = whatis(o) === 'object' || Array.isArray(o);
|
||||
if ( ! could_be ) {
|
||||
return {
|
||||
$: 'error',
|
||||
code: 'invalid-type',
|
||||
message: 'should be object or array',
|
||||
};
|
||||
}
|
||||
|
||||
const intermediate = this.get_intermediate_(o);
|
||||
|
||||
if ( ! intermediate.type ) {
|
||||
return {
|
||||
$: 'error',
|
||||
code: 'missing-type-param',
|
||||
message: 'type parameter is missing',
|
||||
};
|
||||
}
|
||||
|
||||
return this.intermediate_to_standard_(intermediate);
|
||||
}
|
||||
|
||||
intermediate_to_standard_ (intermediate) {
|
||||
const out = {};
|
||||
out.$ = intermediate.type;
|
||||
for ( const k in intermediate.meta ) {
|
||||
out[`$${ k}`] = intermediate.meta[k];
|
||||
}
|
||||
for ( const k in intermediate.body ) {
|
||||
out[k] = intermediate.body[k];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
get_intermediate_ (o) {
|
||||
if ( Array.isArray(o) ) {
|
||||
return this.process_array_(o);
|
||||
}
|
||||
|
||||
if ( o['$'] === '$meta-body' ) {
|
||||
return this.process_structured_(o);
|
||||
}
|
||||
|
||||
return this.process_standard_(o);
|
||||
}
|
||||
|
||||
process_array_ (a) {
|
||||
if ( a.length <= 1 || a.length > 3 ) {
|
||||
return {
|
||||
$: 'error',
|
||||
code: 'invalid-array-length',
|
||||
message: 'tag-typed arrays should have 1-3 elements',
|
||||
};
|
||||
}
|
||||
|
||||
const [type, body = {}, meta = {}] = a;
|
||||
|
||||
return { $: '$', type, body, meta };
|
||||
}
|
||||
|
||||
process_structured_ (o) {
|
||||
if ( ! o.hasOwnProperty('type') ) {
|
||||
return {
|
||||
$: 'error',
|
||||
code: 'missing-type-property',
|
||||
message: 'missing "type" property',
|
||||
};
|
||||
}
|
||||
|
||||
return { $: '$', ...o };
|
||||
}
|
||||
|
||||
process_standard_ (o) {
|
||||
const type = o.$;
|
||||
const meta = {};
|
||||
const body = {};
|
||||
|
||||
for ( const k in o ) {
|
||||
if ( k === '$' ) continue;
|
||||
if ( k.startsWith('$') ) {
|
||||
meta[k.slice(1)] = o[k];
|
||||
} else {
|
||||
body[k] = o[k];
|
||||
}
|
||||
}
|
||||
|
||||
return { $: '$', type, meta, body };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LibTypeTagged;
|
||||
@@ -30,7 +30,6 @@ const winston = require('winston');
|
||||
const { Context } = require('../../util/context');
|
||||
const BaseService = require('../../services/BaseService');
|
||||
const { stringify_log_entry } = require('./lib/log');
|
||||
const { whatis } = require('../../util/langutil');
|
||||
require('winston-daily-rotate-file');
|
||||
|
||||
const WINSTON_LEVELS = {
|
||||
@@ -123,7 +122,7 @@ class LogContext {
|
||||
}
|
||||
for ( const k in fields ) {
|
||||
if (
|
||||
whatis(fields[k]) === 'object' &&
|
||||
fields[k] &&
|
||||
typeof fields[k].toLogFields === 'function'
|
||||
) fields[k] = fields[k].toLogFields();
|
||||
}
|
||||
|
||||
@@ -18,10 +18,7 @@
|
||||
*/
|
||||
const { get_dir_size, id2path, get_user, invalidate_cached_user_by_id } = require('../../helpers');
|
||||
const BaseService = require('../../services/BaseService');
|
||||
|
||||
const { DB_WRITE } = require('../../services/database/consts');
|
||||
const { Context } = require('../../util/context');
|
||||
const { nou } = require('../../util/langutil');
|
||||
|
||||
// TODO: expose to a utility library
|
||||
class UserParameter {
|
||||
@@ -99,9 +96,6 @@ class SizeService extends BaseService {
|
||||
|
||||
// TODO: remove fs arg and update all calls
|
||||
async add_node_size (fs, node, user, factor = 1) {
|
||||
const {
|
||||
fsEntryService,
|
||||
} = Context.get('services').values;
|
||||
|
||||
let sz;
|
||||
if ( node.entry.is_dir ) {
|
||||
@@ -125,7 +119,7 @@ class SizeService extends BaseService {
|
||||
return this.global_config.available_device_storage;
|
||||
}
|
||||
|
||||
if ( nou(user.free_storage) ) {
|
||||
if ( !user.free_storage && user.free_storage !== 0 ) {
|
||||
return this.global_config.storage_capacity;
|
||||
}
|
||||
|
||||
@@ -160,7 +154,7 @@ class SizeService extends BaseService {
|
||||
|
||||
const fields_ = Object.keys(entry);
|
||||
const fields = fields_.join(', ');
|
||||
const placeholders = fields_.map(f => '?').join(', ');
|
||||
const placeholders = fields_.map(_ => '?').join(', ');
|
||||
const values = fields_.map(f => entry[f]);
|
||||
|
||||
try {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
* 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 { nou } = require('../../util/langutil');
|
||||
const { Eq, IsNotNull } = require('../query/query');
|
||||
const { BaseES } = require('./BaseES');
|
||||
|
||||
@@ -49,7 +48,7 @@ class NotificationES extends BaseES {
|
||||
if ( typeof value === 'string' ) {
|
||||
value = JSON.parse(value);
|
||||
}
|
||||
if ( nou(value) ) {
|
||||
if ( ! value ) {
|
||||
value = {};
|
||||
}
|
||||
await entity.set('value', value);
|
||||
|
||||
@@ -28,21 +28,16 @@ class ProtectedAppES extends BaseES {
|
||||
const actor = Context.get('actor');
|
||||
const services = Context.get('services');
|
||||
|
||||
const to_delete = [];
|
||||
for ( let i = 0 ; i < results.length ; i++ ) {
|
||||
const entity = results[i];
|
||||
|
||||
if ( ! await this.check_({ actor, services }, entity) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
to_delete.push(i);
|
||||
results[i] = undefined;
|
||||
}
|
||||
|
||||
const svc_utilArray = services.get('util-array');
|
||||
svc_utilArray.remove_marked_items(to_delete, results);
|
||||
|
||||
return results;
|
||||
return results.filter(e => e !== undefined);
|
||||
}
|
||||
|
||||
async read (uid) {
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
const { get_user } = require('../../helpers');
|
||||
const { AppUnderUserActorType, UserActorType } = require('../../services/auth/Actor');
|
||||
const { Context } = require('../../util/context');
|
||||
const { nou } = require('../../util/langutil');
|
||||
const { BaseES } = require('./BaseES');
|
||||
|
||||
class SetOwnerES extends BaseES {
|
||||
@@ -65,7 +64,7 @@ class SetOwnerES extends BaseES {
|
||||
},
|
||||
async _sanitize_owner (entity) {
|
||||
let owner = await entity.get('owner');
|
||||
if ( nou(owner) ) return null;
|
||||
if ( ! owner ) return null;
|
||||
owner = get_user({ id: owner });
|
||||
await entity.set('owner', owner);
|
||||
},
|
||||
|
||||
@@ -22,7 +22,6 @@ const { FileFacade } = require('../../services/drivers/FileFacade');
|
||||
const { TypeSpec } = require('../../services/drivers/meta/Construct');
|
||||
const { TypedValue } = require('../../services/drivers/meta/Runtime');
|
||||
const { Context } = require('../../util/context');
|
||||
const { whatis } = require('../../util/langutil');
|
||||
const { TeePromise } = require('@heyputer/putility').libs.promise;
|
||||
const { valid_file_size } = require('../../util/validutil');
|
||||
|
||||
@@ -143,12 +142,12 @@ _handle_multipart = async (req) => {
|
||||
if ( ! Object.prototype.hasOwnProperty.call(dst, key_parts[i]) ) {
|
||||
dst[key_parts[i]] = {};
|
||||
}
|
||||
if ( whatis(dst[key_parts[i]]) !== 'object' ) {
|
||||
if ( !dst[key_parts[i]] || typeof dst[key_parts[i]] !== 'object' || Array.isArray(dst[key_parts[i]]) ) {
|
||||
throw new Error(`Tried to set member of non-object: ${key_parts[i]} in ${fieldname}`);
|
||||
}
|
||||
dst = dst[key_parts[i]];
|
||||
}
|
||||
if ( whatis(value) === 'object' && value.$ === 'file' ) {
|
||||
if ( value && value.$ === 'file' ) {
|
||||
const fileinfo = value;
|
||||
const { v: size, ok: size_ok } =
|
||||
valid_file_size(fileinfo.size);
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
*/
|
||||
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
const { can } = require('../util/langutil');
|
||||
const BaseService = require('./BaseService');
|
||||
|
||||
/**
|
||||
@@ -157,7 +156,7 @@ class CleanEmailService extends BaseService {
|
||||
email = this.clean(email);
|
||||
const config = this.global_config;
|
||||
|
||||
if ( can(config.blocked_email_domains, 'iterate') ) {
|
||||
if ( Array.isArray(config.blocked_email_domains) ) {
|
||||
for ( const suffix of config.blocked_email_domains ) {
|
||||
if ( email.endsWith(suffix) ) {
|
||||
return false;
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
// METADATA // {"ai-commented":{"service":"claude"}}
|
||||
const { Context } = require('../util/context');
|
||||
const { whatis } = require('../util/langutil');
|
||||
const { PermissionUtil } = require('./auth/permissionUtils.mjs');
|
||||
const BaseService = require('./BaseService');
|
||||
|
||||
@@ -84,7 +83,7 @@ class FeatureFlagService extends BaseService {
|
||||
let value;
|
||||
const options = {};
|
||||
for ( const arg of a ) {
|
||||
if ( whatis(arg) === 'object' ) {
|
||||
if ( arg && typeof arg === 'object' && !Array.isArray(arg) ) {
|
||||
Object.assign(options, arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
const { APIError } = require('openai');
|
||||
const configurable_auth = require('../middleware/configurable_auth');
|
||||
const { Endpoint } = require('../util/expressutil');
|
||||
const { whatis } = require('../util/langutil');
|
||||
const BaseService = require('./BaseService');
|
||||
|
||||
/**
|
||||
@@ -86,18 +85,18 @@ class PermissionAPIService extends BaseService {
|
||||
|
||||
const extra = req.body.extra ?? {};
|
||||
const metadata = req.body.metadata ?? {};
|
||||
if ( whatis(extra) !== 'object' ) {
|
||||
if ( !extra || typeof extra !== 'object' || Array.isArray(extra) ) {
|
||||
throw APIError.create('field_invalid', null, {
|
||||
key: 'extra',
|
||||
expected: 'object',
|
||||
got: whatis(extra),
|
||||
got: extra,
|
||||
});
|
||||
}
|
||||
if ( whatis(metadata) !== 'object' ) {
|
||||
if ( !metadata || typeof metadata !== 'object' || Array.isArray(metadata) ) {
|
||||
throw APIError.create('field_invalid', null, {
|
||||
key: 'metadata',
|
||||
expected: 'object',
|
||||
got: whatis(metadata),
|
||||
got: metadata,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -135,21 +134,21 @@ class PermissionAPIService extends BaseService {
|
||||
throw APIError.create('forbidden');
|
||||
}
|
||||
|
||||
if ( whatis(req.body.users) !== 'array' ) {
|
||||
if ( ! Array.isArray(req.body.users) ) {
|
||||
throw APIError.create('field_invalid', null, {
|
||||
key: 'users',
|
||||
expected: 'array',
|
||||
got: whatis(req.body.users),
|
||||
got: req.body.users,
|
||||
});
|
||||
}
|
||||
|
||||
for ( let i = 0 ; i < req.body.users.length ; i++ ) {
|
||||
const value = req.body.users[i];
|
||||
if ( whatis(value) === 'string' ) continue;
|
||||
if ( typeof value === 'string' ) continue;
|
||||
throw APIError.create('field_invalid', null, {
|
||||
key: `users[${i}]`,
|
||||
expected: 'string',
|
||||
got: whatis(value),
|
||||
got: value,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -184,21 +183,21 @@ class PermissionAPIService extends BaseService {
|
||||
throw APIError.create('forbidden');
|
||||
}
|
||||
|
||||
if ( whatis(req.body.users) !== 'array' ) {
|
||||
if ( Array.isArray(req.body.users) ) {
|
||||
throw APIError.create('field_invalid', null, {
|
||||
key: 'users',
|
||||
expected: 'array',
|
||||
got: whatis(req.body.users),
|
||||
got: req.body.users,
|
||||
});
|
||||
}
|
||||
|
||||
for ( let i = 0 ; i < req.body.users.length ; i++ ) {
|
||||
const value = req.body.users[i];
|
||||
if ( whatis(value) === 'string' ) continue;
|
||||
if ( typeof value === 'string' ) continue;
|
||||
throw APIError.create('field_invalid', null, {
|
||||
key: `users[${i}]`,
|
||||
expected: 'string',
|
||||
got: whatis(value),
|
||||
got: value,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,7 @@
|
||||
const APIError = require('../api/APIError');
|
||||
const { get_user } = require('../helpers');
|
||||
const configurable_auth = require('../middleware/configurable_auth');
|
||||
const featureflag = require('../middleware/featureflag.js');
|
||||
const { Endpoint } = require('../util/expressutil');
|
||||
const { whatis } = require('../util/langutil');
|
||||
const { Actor, UserActorType } = require('./auth/Actor');
|
||||
const BaseService = require('./BaseService');
|
||||
const { DB_WRITE } = require('./database/consts');
|
||||
@@ -365,7 +363,7 @@ class ShareService extends BaseService {
|
||||
throw new Error('email must be a string');
|
||||
}
|
||||
// track: type check
|
||||
if ( whatis(data) !== 'object' ) {
|
||||
if ( !data || typeof data !== 'object' || Array.isArray(data) ) {
|
||||
throw new Error('data must be an object');
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
// METADATA // {"ai-commented":{"service":"xai"}}
|
||||
const { LLRead } = require('../filesystem/ll_operations/ll_read');
|
||||
const { Context } = require('../util/context');
|
||||
const { whatis } = require('../util/langutil');
|
||||
const { stream_to_buffer } = require('../util/streamutil');
|
||||
const BaseService = require('./BaseService');
|
||||
|
||||
@@ -46,23 +45,25 @@ class SystemDataService extends BaseService {
|
||||
* For special objects with a '$' property, it performs dereferencing.
|
||||
*/
|
||||
async interpret (data) {
|
||||
if ( whatis(data) === 'object' && data.$ ) {
|
||||
if ( data?.$ ) {
|
||||
return await this.#dereference(data);
|
||||
}
|
||||
if ( whatis(data) === 'object' ) {
|
||||
const new_o = {};
|
||||
for ( const k in data ) {
|
||||
new_o[k] = await this.interpret(data[k]);
|
||||
}
|
||||
return new_o;
|
||||
}
|
||||
if ( whatis(data) === 'array' ) {
|
||||
|
||||
if ( Array.isArray(data) ) {
|
||||
const new_a = [];
|
||||
for ( const v of data ) {
|
||||
new_a.push(await this.interpret(v));
|
||||
}
|
||||
return new_a;
|
||||
}
|
||||
if ( data && typeof data === 'object' ) {
|
||||
const new_o = {};
|
||||
for ( const k in data ) {
|
||||
new_o[k] = await this.interpret(data[k]);
|
||||
}
|
||||
return new_o;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@@ -275,52 +275,4 @@ Returns the default model identifier for the XAI service
|
||||
|
||||
This module has external relative imports. When these are
|
||||
removed it may become possible to move this module to an
|
||||
extension.
|
||||
|
||||
**Imports:**
|
||||
- `../../api/APIError`
|
||||
- `../../services/auth/PermissionService`
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../services/database/consts`
|
||||
- `../../services/drivers/meta/Construct`
|
||||
- `../../services/drivers/meta/Runtime`
|
||||
- `../../util/context`
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../services/drivers/meta/Runtime`
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../api/APIError`
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../util/langutil`
|
||||
- `../../services/drivers/meta/Runtime`
|
||||
- `../../api/APIError`
|
||||
- `../../util/promise`
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../services/drivers/meta/Runtime`
|
||||
- `../../util/langutil`
|
||||
- `../../util/promise`
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../services/drivers/meta/Runtime`
|
||||
- `../../util/langutil`
|
||||
- `../../util/promise`
|
||||
- `../../api/APIError`
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../services/drivers/meta/Runtime`
|
||||
- `../../util/context`
|
||||
- `../../util/smolutil`
|
||||
- `../../util/langutil`
|
||||
- `../../util/promise`
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../services/drivers/meta/Runtime`
|
||||
- `../../util/context`
|
||||
- `../../config`
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../services/drivers/meta/Runtime`
|
||||
- `../../util/langutil`
|
||||
- `../../util/promise`
|
||||
- `../../services/BaseService` (use.BaseService)
|
||||
- `../../util/langutil`
|
||||
- `../../services/drivers/meta/Runtime`
|
||||
- `../../util/promise`
|
||||
extension.
|
||||
@@ -1,4 +1,3 @@
|
||||
import { whatis } from '../../../util/langutil.js';
|
||||
|
||||
/**
|
||||
* Normalizes a single message into a standardized format with role and content array.
|
||||
@@ -22,7 +21,7 @@ export const normalize_single_message = (message, params = {}) => {
|
||||
content: [message],
|
||||
};
|
||||
}
|
||||
if ( whatis(message) !== 'object' ) {
|
||||
if ( !message || typeof message !== 'object' || Array.isArray(message) ) {
|
||||
throw new Error('each message must be a string or object');
|
||||
}
|
||||
if ( ! message.role ) {
|
||||
@@ -45,18 +44,18 @@ export const normalize_single_message = (message, params = {}) => {
|
||||
throw new Error('each message must have a \'content\' property');
|
||||
}
|
||||
}
|
||||
if ( whatis(message.content) !== 'array' ) {
|
||||
if ( ! Array.isArray(message.content) ) {
|
||||
message.content = [message.content];
|
||||
}
|
||||
// Coerce each content block into an object
|
||||
for ( let i = 0 ; i < message.content.length ; i++ ) {
|
||||
if ( whatis(message.content[i]) === 'string' ) {
|
||||
if ( typeof message.content[i] === 'string' ) {
|
||||
message.content[i] = {
|
||||
type: 'text',
|
||||
text: message.content[i],
|
||||
};
|
||||
}
|
||||
if ( whatis(message.content[i]) !== 'object' ) {
|
||||
if ( !message || typeof message.content[i] !== 'object' || Array.isArray(message.content[i]) ) {
|
||||
throw new Error('each message content item must be a string or object');
|
||||
}
|
||||
if ( typeof message.content[i].text === 'string' && !message.content[i].type ) {
|
||||
@@ -158,22 +157,22 @@ export const extract_and_remove_system_messages = (messages) => {
|
||||
*/
|
||||
export const extract_text = (messages) => {
|
||||
return messages.map(m => {
|
||||
if ( whatis(m) === 'string' ) {
|
||||
if ( typeof m === 'string' ) {
|
||||
return m;
|
||||
}
|
||||
if ( whatis(m) !== 'object' ) {
|
||||
if ( !m || typeof m !== 'object' || Array.isArray(m) ) {
|
||||
return '';
|
||||
}
|
||||
if ( whatis(m.content) === 'array' ) {
|
||||
if ( Array.isArray(m.content) ) {
|
||||
return m.content.map(c => c.text).join(' ');
|
||||
}
|
||||
if ( whatis(m.content) === 'string' ) {
|
||||
if ( typeof m.content === 'string' ) {
|
||||
return m.content;
|
||||
} else {
|
||||
const is_text_type = m.content.type === 'text' ||
|
||||
!Object.prototype.hasOwnProperty.call(m.content, 'type');
|
||||
if ( is_text_type ) {
|
||||
if ( whatis(m.content.text) !== 'string' ) {
|
||||
if ( typeof m.content.text !== 'string' ) {
|
||||
throw new Error('text content must be a string');
|
||||
}
|
||||
return m.content.text;
|
||||
|
||||
@@ -24,7 +24,6 @@ const { Context } = require('../../util/context');
|
||||
const APIError = require('../../api/APIError');
|
||||
const { DB_WRITE } = require('../database/consts');
|
||||
const { UUIDFPE } = require('../../util/uuidfpe');
|
||||
const { nou } = require('../../util/langutil');
|
||||
|
||||
// This constant defines the namespace used for generating app UUIDs from their origins
|
||||
const APP_ORIGIN_UUID_NAMESPACE = '33de3768-8ee0-43e9-9e73-db192b97a5d8';
|
||||
@@ -102,7 +101,7 @@ class AuthService extends BaseService {
|
||||
|
||||
const user = await get_user({ uuid: decoded.user_uid });
|
||||
|
||||
if ( nou(user) ) {
|
||||
if ( ! user ) {
|
||||
throw APIError.create('user_not_found');
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ const BaseService = require('../BaseService');
|
||||
const { PermissionUtil } = require('../auth/permissionUtils.mjs');
|
||||
const { Invoker } = require('../../../../putility/src/libs/invoker');
|
||||
const { get_user } = require('../../helpers');
|
||||
const { whatis } = require('../../util/langutil');
|
||||
const { AdvancedBase } = require('@heyputer/putility');
|
||||
|
||||
const strutil = require('@heyputer/putility').libs.string;
|
||||
@@ -578,7 +577,7 @@ class DriverService extends BaseService {
|
||||
throw svc_apiError.create('method_not_found', { interface_name, method_name });
|
||||
}
|
||||
|
||||
if ( method.hasOwnProperty('default_parameter') && whatis(args) !== 'object' ) {
|
||||
if ( Object.prototype.hasOwnProperty.call(method, 'default_parameter') && (typeof args !== 'object' || Array.isArray(args)) ) {
|
||||
args = { [method.default_parameter]: args };
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const APIError = require('../../api/APIError');
|
||||
const { Sequence } = require('../../codex/Sequence');
|
||||
const config = require('../../config');
|
||||
const { WorkList } = require('../../util/workutil');
|
||||
|
||||
const { processSharesSequence } = require('./share/process_shares.js');
|
||||
const { UsernameNotifSelector } = require('../../services/NotificationService');
|
||||
const { quot } = require('@heyputer/putility').libs.string;
|
||||
|
||||
@@ -92,7 +92,7 @@ module.exports = new Sequence([
|
||||
a.values({ recipients_work, shares_work });
|
||||
},
|
||||
require('./share/process_recipients.js'),
|
||||
require('./share/process_shares.js'),
|
||||
processSharesSequence,
|
||||
function abort_on_error_if_mode_is_strict (a) {
|
||||
const strict_mode = a.get('strict_mode');
|
||||
if ( ! strict_mode ) return;
|
||||
|
||||
@@ -17,14 +17,14 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const APIError = require('../../../api/APIError');
|
||||
const { Sequence } = require('../../../codex/Sequence');
|
||||
const config = require('../../../config');
|
||||
const { get_user, get_app } = require('../../../helpers');
|
||||
const { PermissionUtil } = require('../../../services/auth/permissionUtils.mjs');
|
||||
const FSNodeParam = require('../../../api/filesystem/FSNodeParam');
|
||||
const { TYPE_DIRECTORY } = require('../../../filesystem/FSNodeContext');
|
||||
const { MANAGE_PERM_PREFIX } = require('../../../services/auth/permissionConts.mjs');
|
||||
import APIError from '../../../api/APIError.js';
|
||||
import { Sequence } from '../../../codex/Sequence.js';
|
||||
import config from '../../../config.js';
|
||||
import { get_user, get_app } from '../../../helpers.js';
|
||||
import { PermissionUtil } from '../../../services/auth/permissionUtils.mjs';
|
||||
import FSNodeParam from '../../../api/filesystem/FSNodeParam.js';
|
||||
import { TYPE_DIRECTORY } from '../../../filesystem/FSNodeContext.js';
|
||||
import { MANAGE_PERM_PREFIX } from '../../../services/auth/permissionConts.mjs';
|
||||
|
||||
/*
|
||||
This code is optimized for editors supporting folding.
|
||||
@@ -38,7 +38,84 @@ const { MANAGE_PERM_PREFIX } = require('../../../services/auth/permissionConts.m
|
||||
}
|
||||
*/
|
||||
|
||||
module.exports = new Sequence({
|
||||
// TODO DS: simplify these into the method
|
||||
const is_plain_object = (value) =>
|
||||
value !== null && typeof value === 'object' && !Array.isArray(value);
|
||||
|
||||
const error = (code, message) => ({ $: 'error', code, message });
|
||||
|
||||
const normalize_body = (body) => {
|
||||
if ( body === undefined ) return {};
|
||||
if ( is_plain_object(body) ) return body;
|
||||
return { value: body };
|
||||
};
|
||||
|
||||
const normalize_meta = (meta) => is_plain_object(meta) ? meta : {};
|
||||
|
||||
const to_standard = (type, body = {}, meta = {}) => {
|
||||
if ( ! type ) {
|
||||
return error('missing-type-param', 'type parameter is missing');
|
||||
}
|
||||
|
||||
const prefixed_meta = Object.fromEntries(Object.entries(meta).map(([k, v]) => [`$${k}`, v]));
|
||||
|
||||
return { $: type, ...prefixed_meta, ...body };
|
||||
};
|
||||
|
||||
const process_array = (value) => {
|
||||
if ( value.length <= 1 || value.length > 3 ) {
|
||||
return error('invalid-array-length',
|
||||
'tag-typed arrays should have 1-3 elements');
|
||||
}
|
||||
|
||||
const [type, raw_body, raw_meta] = value;
|
||||
return to_standard(type, normalize_body(raw_body), normalize_meta(raw_meta));
|
||||
};
|
||||
|
||||
const process_structured = (value) => {
|
||||
if ( ! Object.prototype.hasOwnProperty.call(value, 'type') ) {
|
||||
return error('missing-type-property', 'missing "type" property');
|
||||
}
|
||||
|
||||
return to_standard(value.type,
|
||||
normalize_body(value.body),
|
||||
normalize_meta(value.meta));
|
||||
};
|
||||
|
||||
const process_standard = (value) => {
|
||||
const meta = {};
|
||||
const body = {};
|
||||
|
||||
for ( const [key, val] of Object.entries(value) ) {
|
||||
if ( key === '$' ) continue;
|
||||
if ( key.startsWith('$') ) {
|
||||
meta[key.slice(1)] = val;
|
||||
} else {
|
||||
body[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
return to_standard(value.$, body, meta);
|
||||
};
|
||||
|
||||
const parseTypeTagged = (value) => {
|
||||
const is_object_like = value !== null && typeof value === 'object';
|
||||
if ( !is_object_like && !Array.isArray(value) ) {
|
||||
return error('invalid-type', 'should be object or array');
|
||||
}
|
||||
|
||||
if ( Array.isArray(value) ) {
|
||||
return process_array(value);
|
||||
}
|
||||
|
||||
if ( value.$ === '$meta-body' ) {
|
||||
return process_structured(value);
|
||||
}
|
||||
|
||||
return process_standard(value);
|
||||
};
|
||||
|
||||
export const processSharesSequence = new Sequence({
|
||||
name: 'process shares',
|
||||
beforeEach (a) {
|
||||
const { shares_work } = a.values();
|
||||
@@ -48,13 +125,11 @@ module.exports = new Sequence({
|
||||
function validate_share_types (a) {
|
||||
const { result, shares_work } = a.values();
|
||||
|
||||
const lib_typeTagged = a.iget('services').get('lib-type-tagged');
|
||||
|
||||
for ( const item of shares_work.list() ) {
|
||||
const { i } = item;
|
||||
let { value } = item;
|
||||
|
||||
const thing = lib_typeTagged.process(value);
|
||||
const thing = parseTypeTagged(value);
|
||||
if ( thing.$ === 'error' ) {
|
||||
item.invalid = true;
|
||||
result.shares[i] =
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
const APIError = require('../../../api/APIError');
|
||||
const { Sequence } = require('../../../codex/Sequence');
|
||||
const { whatis } = require('../../../util/langutil');
|
||||
|
||||
/*
|
||||
This code is optimized for editors supporting folding.
|
||||
@@ -46,7 +45,7 @@ module.exports = new Sequence({
|
||||
throw APIError.create('field_invalid', null, {
|
||||
key: 'metadata',
|
||||
expected: 'object',
|
||||
got: whatis(metadata),
|
||||
got: metadata,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,7 +67,7 @@ module.exports = new Sequence({
|
||||
throw APIError.create('field_invalid', null, {
|
||||
key: `metadata.${key}`,
|
||||
expected: 'string or number',
|
||||
got: whatis(value),
|
||||
got: value,
|
||||
});
|
||||
}
|
||||
if ( key === 'message' ) {
|
||||
@@ -76,7 +75,7 @@ module.exports = new Sequence({
|
||||
throw APIError.create('field_invalid', null, {
|
||||
key: `metadata.${key}`,
|
||||
expected: 'string',
|
||||
got: whatis(value),
|
||||
got: value,
|
||||
});
|
||||
}
|
||||
if ( value.length > MAX_MESSAGE_STRING ) {
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present 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/>.
|
||||
*/
|
||||
/**
|
||||
* whatis is an alterative to typeof that reports what
|
||||
* the type of the value actually is for real.
|
||||
*/
|
||||
const whatis = thing => {
|
||||
if ( Array.isArray(thing) ) return 'array';
|
||||
if ( thing === null ) return 'null';
|
||||
return typeof thing;
|
||||
};
|
||||
|
||||
const nou = v => v === null || v === undefined;
|
||||
|
||||
const can = (v, ...checking) => {
|
||||
if ( nou(v) ) return false;
|
||||
const capabilities = {};
|
||||
if ( v[Symbol.iterator] ) {
|
||||
capabilities['iterate'] = true;
|
||||
}
|
||||
for ( const to_check of checking ) {
|
||||
if ( ! capabilities[to_check] ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
whatis,
|
||||
nou,
|
||||
can,
|
||||
};
|
||||
@@ -1,12 +1,10 @@
|
||||
const { whatis } = require('./langutil');
|
||||
|
||||
const DO_NOT_DEFINE = Symbol('DO_NOT_DEFINE');
|
||||
|
||||
const createTransformedValues = (input, options = {}, state = {}) => {
|
||||
// initialize state
|
||||
if ( ! state.keys ) state.keys = [];
|
||||
|
||||
if ( whatis(input) === 'array' ) {
|
||||
if ( Array.isArray(input) ) {
|
||||
if ( options.doNotProcessArrays ) {
|
||||
return DO_NOT_DEFINE;
|
||||
}
|
||||
@@ -19,7 +17,7 @@ const createTransformedValues = (input, options = {}, state = {}) => {
|
||||
}
|
||||
return output;
|
||||
}
|
||||
if ( whatis(input) === 'object' ) {
|
||||
if ( input && typeof input === 'object' && !Array.isArray(input) ) {
|
||||
const output = {};
|
||||
Object.setPrototypeOf(output, input);
|
||||
for ( const k in input ) {
|
||||
|
||||
Reference in New Issue
Block a user