diff --git a/src/backend/src/filesystem/batch/BatchExecutor.js b/src/backend/src/filesystem/batch/BatchExecutor.js index 062766a6d..5229fc7f8 100644 --- a/src/backend/src/filesystem/batch/BatchExecutor.js +++ b/src/backend/src/filesystem/batch/BatchExecutor.js @@ -22,7 +22,7 @@ const commands = require('./commands').commands; const APIError = require('../../api/APIError'); const { Context } = require('../../util/context'); const config = require('../../config'); -const { TeePromise } = require('../../util/promise'); +const { TeePromise } = require('@heyputer/putility'); const { WorkUnit } = require('../../modules/core/lib/expect'); class BatchExecutor extends AdvancedBase { diff --git a/src/backend/src/filesystem/hl_operations/hl_write.js b/src/backend/src/filesystem/hl_operations/hl_write.js index c0f711014..dc78996e6 100644 --- a/src/backend/src/filesystem/hl_operations/hl_write.js +++ b/src/backend/src/filesystem/hl_operations/hl_write.js @@ -23,7 +23,7 @@ const StringParam = require("../../api/filesystem/StringParam"); const UserParam = require("../../api/filesystem/UserParam"); const config = require("../../config"); const { chkperm, validate_fsentry_name } = require("../../helpers"); -const { TeePromise } = require("../../util/promise"); +const { TeePromise } = require("@heyputer/putility").libs.promise; const { pausing_tee, logging_stream, offset_write_stream, stream_to_the_void } = require("../../util/streamutil"); const { TYPE_DIRECTORY } = require("../FSNodeContext"); const { LLRead } = require("../ll_operations/ll_read"); diff --git a/src/backend/src/fun/dev-console-ui-utils.js b/src/backend/src/fun/dev-console-ui-utils.js index 102370a1a..27c5392b7 100644 --- a/src/backend/src/fun/dev-console-ui-utils.js +++ b/src/backend/src/fun/dev-console-ui-utils.js @@ -17,7 +17,7 @@ * along with this program. If not, see . */ const config = require('../config'); -const { TeePromise } = require('../util/promise'); +const { TeePromise } = require('@heyputer/putility').libs.promise; const es_import_promise = new TeePromise(); let stringLength; diff --git a/src/backend/src/modules/puterai/ClaudeService.js b/src/backend/src/modules/puterai/ClaudeService.js index b211928a9..36f05cebf 100644 --- a/src/backend/src/modules/puterai/ClaudeService.js +++ b/src/backend/src/modules/puterai/ClaudeService.js @@ -5,7 +5,7 @@ const { whatis } = require("../../util/langutil"); const { PassThrough } = require("stream"); const { TypedValue } = require("../../services/drivers/meta/Runtime"); const APIError = require("../../api/APIError"); -const { TeePromise } = require("../../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; const PUTER_PROMPT = ` You are running on an open-source platform called Puter, diff --git a/src/backend/src/modules/puterai/GroqAIService.js b/src/backend/src/modules/puterai/GroqAIService.js index 010d0f251..454aad74a 100644 --- a/src/backend/src/modules/puterai/GroqAIService.js +++ b/src/backend/src/modules/puterai/GroqAIService.js @@ -3,7 +3,7 @@ const { PassThrough } = require("stream"); const BaseService = require("../../services/BaseService"); const { TypedValue } = require("../../services/drivers/meta/Runtime"); const { nou } = require("../../util/langutil"); -const { TeePromise } = require("../../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; /** diff --git a/src/backend/src/modules/puterai/MistralAIService.js b/src/backend/src/modules/puterai/MistralAIService.js index fc7302653..ccca80fbd 100644 --- a/src/backend/src/modules/puterai/MistralAIService.js +++ b/src/backend/src/modules/puterai/MistralAIService.js @@ -5,7 +5,7 @@ const { TypedValue } = require("../../services/drivers/meta/Runtime"); const { nou } = require("../../util/langutil"); const axios = require('axios'); -const { TeePromise } = require("../../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; /** diff --git a/src/backend/src/modules/puterai/OpenAICompletionService.js b/src/backend/src/modules/puterai/OpenAICompletionService.js index 19c0e2e60..881cd7f62 100644 --- a/src/backend/src/modules/puterai/OpenAICompletionService.js +++ b/src/backend/src/modules/puterai/OpenAICompletionService.js @@ -6,7 +6,7 @@ const { TypedValue } = require('../../services/drivers/meta/Runtime'); const { Context } = require('../../util/context'); const SmolUtil = require('../../util/smolutil'); const { nou } = require('../../util/langutil'); -const { TeePromise } = require('../../util/promise'); +const { TeePromise } = require('@heyputer/putility').libs.promise; /** diff --git a/src/backend/src/modules/puterai/TogetherAIService.js b/src/backend/src/modules/puterai/TogetherAIService.js index 9747cafde..6ca2967b8 100644 --- a/src/backend/src/modules/puterai/TogetherAIService.js +++ b/src/backend/src/modules/puterai/TogetherAIService.js @@ -3,7 +3,7 @@ const { PassThrough } = require("stream"); const BaseService = require("../../services/BaseService"); const { TypedValue } = require("../../services/drivers/meta/Runtime"); const { nou } = require("../../util/langutil"); -const { TeePromise } = require("../../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; /** diff --git a/src/backend/src/modules/puterai/XAIService.js b/src/backend/src/modules/puterai/XAIService.js index cead87281..58e5c9290 100644 --- a/src/backend/src/modules/puterai/XAIService.js +++ b/src/backend/src/modules/puterai/XAIService.js @@ -4,7 +4,7 @@ const BaseService = require("../../services/BaseService"); const { whatis, nou } = require("../../util/langutil"); const { PassThrough } = require("stream"); const { TypedValue } = require("../../services/drivers/meta/Runtime"); -const { TeePromise } = require("../../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; const PUTER_PROMPT = ` You are running on an open-source platform called Puter, diff --git a/src/backend/src/modules/selfhosted/DefaultUserService.js b/src/backend/src/modules/selfhosted/DefaultUserService.js index a0de195f4..774d6cc78 100644 --- a/src/backend/src/modules/selfhosted/DefaultUserService.js +++ b/src/backend/src/modules/selfhosted/DefaultUserService.js @@ -22,7 +22,7 @@ const { NodePathSelector } = require("../../filesystem/node/selectors"); const { surrounding_box } = require("../../fun/dev-console-ui-utils"); const { get_user, invalidate_cached_user } = require("../../helpers"); const { Context } = require("../../util/context"); -const { asyncSafeSetInterval } = require("../../util/promise"); +const { asyncSafeSetInterval } = require('@heyputer/putility').libs.promise; const { buffer_to_stream } = require("../../util/streamutil"); const BaseService = require("../../services/BaseService"); const { Actor, UserActorType } = require("../../services/auth/Actor"); diff --git a/src/backend/src/routers/drivers/call.js b/src/backend/src/routers/drivers/call.js index 9e472d7a2..f0453b995 100644 --- a/src/backend/src/routers/drivers/call.js +++ b/src/backend/src/routers/drivers/call.js @@ -23,7 +23,7 @@ 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("../../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; const { valid_file_size } = require("../../util/validutil"); let _handle_multipart; diff --git a/src/backend/src/routers/filesystem_api/batch/all.js b/src/backend/src/routers/filesystem_api/batch/all.js index 2bcf594d6..96dccdf16 100644 --- a/src/backend/src/routers/filesystem_api/batch/all.js +++ b/src/backend/src/routers/filesystem_api/batch/all.js @@ -23,7 +23,7 @@ const PathResolver = require("./PathResolver"); const { Context } = require("../../../util/context"); const Busboy = require('busboy'); const { BatchExecutor } = require("../../../filesystem/batch/BatchExecutor"); -const { TeePromise } = require("../../../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; const { EWMA, MovingMode } = require("../../../util/opmath"); const { get_app } = require('../../../helpers'); const { valid_file_size } = require("../../../util/validutil"); diff --git a/src/backend/src/routers/filesystem_api/write.js b/src/backend/src/routers/filesystem_api/write.js index de0f4f65b..df4d1366b 100644 --- a/src/backend/src/routers/filesystem_api/write.js +++ b/src/backend/src/routers/filesystem_api/write.js @@ -23,7 +23,7 @@ const { HLWrite } = require('../../filesystem/hl_operations/hl_write.js'); const { boolify } = require('../../util/hl_types.js'); const { Context } = require('../../util/context.js'); const Busboy = require('busboy'); -const { TeePromise } = require('../../util/promise.js'); +const { TeePromise } = require('@heyputer/putility').libs.promise; const APIError = require('../../api/APIError.js'); const { valid_file_size } = require('../../util/validutil.js'); diff --git a/src/backend/src/services/AppInformationService.js b/src/backend/src/services/AppInformationService.js index c38be268c..d82c163b3 100644 --- a/src/backend/src/services/AppInformationService.js +++ b/src/backend/src/services/AppInformationService.js @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -const { asyncSafeSetInterval } = require("../util/promise"); +const { asyncSafeSetInterval } = require('@heyputer/putility').libs.promise; const { MINUTE, SECOND } = require("@heyputer/putility").libs.time; const { origin_from_url } = require("../util/urlutil"); const { DB_READ } = require("./database/consts"); diff --git a/src/backend/src/services/Container.js b/src/backend/src/services/Container.js index c3500be8c..ec82c72a6 100644 --- a/src/backend/src/services/Container.js +++ b/src/backend/src/services/Container.js @@ -21,7 +21,7 @@ const { AdvancedBase } = require("@heyputer/putility"); const config = require("../config"); const { Context } = require("../util/context"); const { CompositeError } = require("../util/errorutil"); -const { TeePromise } = require("../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; // 17 lines of code instead of an entire dependency-injection framework /** diff --git a/src/backend/src/services/LocalDiskStorageService.js b/src/backend/src/services/LocalDiskStorageService.js index 464b64fa1..a9d01080e 100644 --- a/src/backend/src/services/LocalDiskStorageService.js +++ b/src/backend/src/services/LocalDiskStorageService.js @@ -18,7 +18,7 @@ * along with this program. If not, see . */ const { LocalDiskStorageStrategy } = require("../filesystem/strategies/storage_a/LocalDiskStorageStrategy"); -const { TeePromise } = require("../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; const { progress_stream, size_limit_stream } = require("../util/streamutil"); const BaseService = require("./BaseService"); diff --git a/src/backend/src/services/NotificationService.js b/src/backend/src/services/NotificationService.js index 76edcb2b0..99905e636 100644 --- a/src/backend/src/services/NotificationService.js +++ b/src/backend/src/services/NotificationService.js @@ -20,7 +20,7 @@ const APIError = require("../api/APIError"); const auth2 = require("../middleware/auth2"); const { Endpoint } = require("../util/expressutil"); -const { TeePromise } = require("../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; const BaseService = require("./BaseService"); const { DB_WRITE } = require("./database/consts"); diff --git a/src/backend/src/services/SUService.js b/src/backend/src/services/SUService.js index 0a7bb6322..c085c3a06 100644 --- a/src/backend/src/services/SUService.js +++ b/src/backend/src/services/SUService.js @@ -1,7 +1,7 @@ // METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}} const { get_user } = require("../helpers"); const { Context } = require("../util/context"); -const { TeePromise } = require("../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; const { Actor, UserActorType } = require("./auth/Actor"); const BaseService = require("./BaseService"); diff --git a/src/backend/src/services/SessionService.js b/src/backend/src/services/SessionService.js index a083426ba..551d7380f 100644 --- a/src/backend/src/services/SessionService.js +++ b/src/backend/src/services/SessionService.js @@ -18,7 +18,7 @@ * along with this program. If not, see . */ const { invalidate_cached_user, get_user } = require("../helpers"); -const { asyncSafeSetInterval } = require("../util/promise"); +const { asyncSafeSetInterval } = require('@heyputer/putility').libs.promise; const { MINUTE, SECOND } = require("@heyputer/putility").libs.time; const BaseService = require("./BaseService"); const { DB_WRITE } = require("./database/consts"); diff --git a/src/backend/src/services/abuse-prevention/EdgeRateLimitService.js b/src/backend/src/services/abuse-prevention/EdgeRateLimitService.js index bb4809950..8d374dac3 100644 --- a/src/backend/src/services/abuse-prevention/EdgeRateLimitService.js +++ b/src/backend/src/services/abuse-prevention/EdgeRateLimitService.js @@ -18,7 +18,7 @@ * along with this program. If not, see . */ const { Context } = require("../../util/context"); -const { asyncSafeSetInterval } = require("../../util/promise"); +const { asyncSafeSetInterval } = require('@heyputer/putility').libs.promise; const { quot } = require('@heyputer/putility').libs.string; const { MINUTE, HOUR } = require('@heyputer/putility').libs.time; diff --git a/src/backend/src/services/fs/FSLockService.js b/src/backend/src/services/fs/FSLockService.js index e787a5c62..aeffff590 100644 --- a/src/backend/src/services/fs/FSLockService.js +++ b/src/backend/src/services/fs/FSLockService.js @@ -18,7 +18,6 @@ * along with this program. If not, see . */ const { RWLock } = require("../../util/lockutil"); -const { TeePromise } = require("../../util/promise"); const BaseService = require("../BaseService"); // Constant representing the read lock mode used for distinguishing between read and write operations. diff --git a/src/backend/src/services/runtime-analysis/ServerHealthService.js b/src/backend/src/services/runtime-analysis/ServerHealthService.js index c64dbbd17..34fadf8b7 100644 --- a/src/backend/src/services/runtime-analysis/ServerHealthService.js +++ b/src/backend/src/services/runtime-analysis/ServerHealthService.js @@ -18,9 +18,8 @@ * along with this program. If not, see . */ const BaseService = require("../BaseService"); -const { SECOND } = require("@heyputer/putility").libs.time; +const { time, promise } = require("@heyputer/putility").libs; const { parse_meminfo } = require("../../util/linux"); -const { asyncSafeSetInterval, TeePromise } = require("../../util/promise"); /** @@ -135,11 +134,11 @@ class ServerHealthService extends BaseService { * @param {none} - No parameters are passed to this method. * @returns {void} */ - asyncSafeSetInterval(async () => { + promise.asyncSafeSetInterval(async () => { this.log.tick('service checks'); const check_failures = []; for ( const { name, fn, chainable } of this.checks_ ) { - const p_timeout = new TeePromise(); + const p_timeout = new promise.TeePromise(); /** * Creates a TeePromise to handle potential timeouts during health checks. * @@ -147,7 +146,7 @@ class ServerHealthService extends BaseService { */ const timeout = setTimeout(() => { p_timeout.reject(new Error('Health check timed out')); - }, 5 * SECOND); + }, 5 * time.SECOND); try { await Promise.race([ fn(), @@ -180,7 +179,7 @@ class ServerHealthService extends BaseService { } this.failures_ = check_failures; - }, 10 * SECOND, null, { + }, 10 * time.SECOND, null, { onBehindSchedule: (drift) => { svc_alarm.create( 'health-checks-behind-schedule', diff --git a/src/backend/src/services/thumbnails/HTTPThumbnailService.js b/src/backend/src/services/thumbnails/HTTPThumbnailService.js index 2ce892377..b4a6dd4d5 100644 --- a/src/backend/src/services/thumbnails/HTTPThumbnailService.js +++ b/src/backend/src/services/thumbnails/HTTPThumbnailService.js @@ -22,7 +22,7 @@ const axios = require('axios'); -const { TeePromise } = require("../../util/promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; const { AdvancedBase } = require('@heyputer/putility'); const FormData = require("form-data"); const { stream_to_the_void, buffer_to_stream } = require('../../util/streamutil'); diff --git a/src/backend/src/traits/SyncFeature.js b/src/backend/src/traits/SyncFeature.js index b88c5ca43..633521cd9 100644 --- a/src/backend/src/traits/SyncFeature.js +++ b/src/backend/src/traits/SyncFeature.js @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -const { Lock } = require("../util/promise"); +const { Lock } = require('@heyputer/putility').libs.promise; class SyncFeature { constructor (method_include_list) { diff --git a/src/backend/src/util/lockutil.js b/src/backend/src/util/lockutil.js index b0862600e..dde83572b 100644 --- a/src/backend/src/util/lockutil.js +++ b/src/backend/src/util/lockutil.js @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -const { TeePromise } = require("./promise"); +const { TeePromise } = require('@heyputer/putility').libs.promise; class RWLock { static TYPE_READ = Symbol('read'); diff --git a/src/backend/src/util/otelutil.js b/src/backend/src/util/otelutil.js index ecebdb469..e8aef304f 100644 --- a/src/backend/src/util/otelutil.js +++ b/src/backend/src/util/otelutil.js @@ -23,7 +23,7 @@ const { context, trace, SpanStatusCode } = require('@opentelemetry/api'); const { Context } = require('./context'); -const { TeePromise } = require('./promise'); +const { TeePromise } = require('@heyputer/putility').libs.promise; /* parallel span example from GPT-4: diff --git a/src/backend/src/util/promise.js b/src/backend/src/util/promise.js deleted file mode 100644 index 137b19c37..000000000 --- a/src/backend/src/util/promise.js +++ /dev/null @@ -1,156 +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 . - */ -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} 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); - } - } -} - -module.exports = { - TeePromise, - Lock, - asyncSafeSetInterval, -}; diff --git a/src/backend/src/util/streamutil.js b/src/backend/src/util/streamutil.js index 2f75f8fb2..bd06b7af0 100644 --- a/src/backend/src/util/streamutil.js +++ b/src/backend/src/util/streamutil.js @@ -17,7 +17,7 @@ * along with this program. If not, see . */ const { PassThrough, Readable, Transform } = require('stream'); -const { TeePromise } = require('./promise'); +const { TeePromise } = require('@heyputer/putility').libs.promise; const { EWMA } = require('./opmath'); class StreamBuffer extends TeePromise {