@ayonli/jsext
Advanced tools
Comparing version 0.6.2 to 0.6.3
'use strict'; | ||
if (!Symbol.asyncIterator) { | ||
// @ts-ignore | ||
Symbol.asyncIterator = Symbol("Symbol.asyncIterator"); | ||
@@ -9,3 +10,4 @@ } | ||
* Checks if the given object is an IteratorLike (implemented `next`). | ||
* @returns {obj is { next: Function }} | ||
* @param {any} obj | ||
* @returns {obj is { [x: string | symbol]: any; next: Function }} | ||
*/ | ||
@@ -23,2 +25,3 @@ function isIteratorLike(obj) { | ||
* `@@iterator` and `next`). | ||
* @param {any} obj | ||
*/ | ||
@@ -33,2 +36,3 @@ function isIterableIterator(obj) { | ||
* both `@@asyncIterator` and `next`). | ||
* @param {any} obj | ||
* @returns {obj is AsyncIterableIterator<any>} | ||
@@ -43,2 +47,3 @@ */ | ||
* Checks if the given object is a Generator. | ||
* @param {any} obj | ||
* @returns {obj is Generator} | ||
@@ -53,2 +58,3 @@ */ | ||
* Checks if the given object is an AsyncGenerator. | ||
* @param {any} obj | ||
* @returns {obj is AsyncGenerator} | ||
@@ -61,2 +67,5 @@ */ | ||
/** | ||
* @param {any} obj | ||
*/ | ||
function hasGeneratorSpecials(obj) { | ||
@@ -63,0 +72,0 @@ return typeof obj.return === "function" |
@@ -15,2 +15,3 @@ 'use strict'; | ||
var example = require('./example.js'); | ||
var deprecate = require('./deprecate.js'); | ||
@@ -35,2 +36,3 @@ const AsyncFunction = (async function () { }).constructor; | ||
example: example.default, | ||
deprecate: deprecate.default, | ||
}; | ||
@@ -52,2 +54,3 @@ | ||
exports.example = example.default; | ||
exports.deprecate = deprecate.default; | ||
exports.AsyncFunction = AsyncFunction; | ||
@@ -54,0 +57,0 @@ exports.AsyncGeneratorFunction = AsyncGeneratorFunction; |
@@ -140,3 +140,3 @@ 'use strict'; | ||
* | ||
* When parsing JSON via `parseAs`, this property is guaranteed to be of the given type. | ||
* When parsing JSON via {@link parseAs}, this property is guaranteed to be of the given type. | ||
* | ||
@@ -143,0 +143,0 @@ * NOTE: this decorator only supports TypeScript's `experimentalDecorators`. |
'use strict'; | ||
/** | ||
* Returns `true` if the specified object has the indicated property as its own property. | ||
* If the property is inherited, or does not exist, the function returns `false`. | ||
*/ | ||
function hasOwn(obj, key) { | ||
@@ -79,3 +83,3 @@ return Object.prototype.hasOwnProperty.call(obj, key); | ||
/** | ||
* Returns `true` if the given value is valid. Thee following values are considered invalid: | ||
* Returns `true` if the given value is valid. The following values are considered invalid: | ||
* | ||
@@ -82,0 +86,0 @@ * - `undefined` |
@@ -30,3 +30,3 @@ 'use strict'; | ||
} | ||
/** Blocks the context until the test is passed. */ | ||
/** Blocks the context until the test passes. */ | ||
async function until(test) { | ||
@@ -33,0 +33,0 @@ while ((await test()) === false) { |
@@ -38,3 +38,3 @@ 'use strict'; | ||
* Processes data sequentially by the given `handler` function and prevents concurrency | ||
* conflicts, it returns a queue instance that we can push data into. | ||
* conflicts, it returns a {@link Queue} instance that we can push data into. | ||
* | ||
@@ -41,0 +41,0 @@ * @param bufferSize The maximum capacity of the underlying channel, once reached, the push |
133
cjs/run.js
@@ -6,3 +6,5 @@ 'use strict'; | ||
var number_index = require('./number/index.js'); | ||
var string_index = require('./string/index.js'); | ||
var chan = require('./chan.js'); | ||
var deprecate = require('./deprecate.js'); | ||
@@ -12,13 +14,2 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; | ||
const isNode = typeof process === "object" && !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node); | ||
/** | ||
* The maximum number of workers allowed to exist at the same time. | ||
* | ||
* The primary purpose of the workers is not mean to run tasks in parallel, but run them in separate | ||
* from the main thread, so that aborting tasks can be achieved by terminating the worker thread and | ||
* it will not affect the main thread. | ||
* | ||
* That said, the worker thread can still be used to achieve parallelism, but it should be noticed | ||
* that only the numbers of tasks that equals to the CPU core numbers will be run at the same time. | ||
*/ | ||
const maxWorkerNum = 16; | ||
const workerIdCounter = number_index.sequence(1, Number.MAX_SAFE_INTEGER, 1, true); | ||
@@ -29,46 +20,23 @@ let workerPool = []; | ||
const workerConsumerQueue = []; | ||
/** | ||
* Runs a `script` in a worker thread or child process that can be aborted during runtime. | ||
* | ||
* In Node.js and Bun, the `script` can be either a CommonJS module or an ES module, and is relative | ||
* to the current working directory if not absolute. | ||
* | ||
* In browser and Deno, the `script` can only be an ES module, and is relative to the current URL | ||
* (or working directory for Deno) if not absolute. | ||
* | ||
* @example | ||
* ```ts | ||
* const job1 = await run("./job-example.mjs", ["World"]); | ||
* console.log(await job1.result()); // Hello, World | ||
* ``` | ||
* | ||
* @example | ||
* ```ts | ||
* const job2 = await run<string, [string[]]>("./job-example.mjs", [["foo", "bar"]], { | ||
* fn: "sequence", | ||
* }); | ||
* for await (const word of job2.iterate()) { | ||
* console.log(word); | ||
* } | ||
* // output: | ||
* // foo | ||
* // bar | ||
* ``` | ||
* | ||
* @example | ||
* ```ts | ||
* const job3 = await run<string, [string]>("./job-example.mjs", ["foobar"], { | ||
* fn: "takeTooLong", | ||
* }); | ||
* await job3.abort(); | ||
* const [err, res] = await _try(job3.result()); | ||
* console.assert(err === null); | ||
* console.assert(res === undefined); | ||
* ``` | ||
*/ | ||
async function run(script, args = undefined, options = undefined) { | ||
var _a, _b; | ||
let _script = ""; | ||
if (typeof script === "function") { | ||
let str = script.toString(); | ||
let start = str.lastIndexOf("("); | ||
if (start === -1) { | ||
throw new TypeError("the given script is not a dynamic import expression"); | ||
} | ||
else { | ||
start += 1; | ||
const end = str.indexOf(")", start); | ||
_script = string_index.trim(str.slice(start, end), ` '"\'`); | ||
} | ||
} | ||
else { | ||
_script = script; | ||
} | ||
const msg = { | ||
type: "ffi", | ||
script, | ||
script: _script, | ||
baseUrl: "", | ||
@@ -87,2 +55,6 @@ fn: (options === null || options === void 0 ? void 0 : options.fn) || "default", | ||
} | ||
if (options === null || options === void 0 ? void 0 : options.workerEntry) { | ||
deprecate.default("options.workerEntry", run, "set `run.workerEntry` instead"); | ||
} | ||
let entry = (options === null || options === void 0 ? void 0 : options.workerEntry) || run.workerEntry; | ||
let error = null; | ||
@@ -174,12 +146,20 @@ let result; | ||
if (isNode) { | ||
let entry = options === null || options === void 0 ? void 0 : options.workerEntry; | ||
if (!entry) { | ||
const path = await import('path'); | ||
const { fileURLToPath } = await import('url'); | ||
const dirname = path.dirname(fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('run.js', document.baseURI).href)))); | ||
if (["cjs", "esm"].includes(path.basename(dirname))) { // compiled | ||
entry = path.join(path.dirname(dirname), "bundle", "worker.mjs"); | ||
const _filename = fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('run.js', document.baseURI).href))); | ||
const _dirname = path.dirname(_filename); | ||
if (_filename === process.argv[1]) { | ||
// The code is bundled, try the worker entry in node_modules (if it exists). | ||
entry = "./node_modules/@ayonli/jsext/bundle/worker.mjs"; | ||
} | ||
else if ([ | ||
path.join("jsext", "cjs"), | ||
path.join("jsext", "esm"), | ||
path.join("jsext", "bundle") | ||
].some(path => _dirname.endsWith(path))) { // compiled | ||
entry = path.join(path.dirname(_dirname), "bundle", "worker.mjs"); | ||
} | ||
else { | ||
entry = path.join(dirname, "worker.mjs"); | ||
entry = path.join(_dirname, "worker.mjs"); | ||
} | ||
@@ -198,3 +178,3 @@ } | ||
} | ||
else if (workerPool.length < maxWorkerNum) { | ||
else if (workerPool.length < run.maxWorkers) { | ||
const { fork } = await import('child_process'); | ||
@@ -236,3 +216,3 @@ const isPrior14 = parseInt(process.version.slice(1)) < 14; | ||
workerConsumerQueue.push(resolve); | ||
}).then(() => run(script, args, options)); | ||
}).then(() => run(_script, args, options)); | ||
} | ||
@@ -263,3 +243,3 @@ release = () => { | ||
} | ||
else if (workerPool.length < maxWorkerNum) { | ||
else if (workerPool.length < run.maxWorkers) { | ||
const { Worker } = await import('worker_threads'); | ||
@@ -291,3 +271,3 @@ worker = new Worker(entry); | ||
workerConsumerQueue.push(resolve); | ||
}).then(() => run(script, args, options)); | ||
}).then(() => run(_script, args, options)); | ||
} | ||
@@ -318,14 +298,22 @@ release = () => { | ||
} | ||
else if (workerPool.length < maxWorkerNum) { | ||
else if (workerPool.length < run.maxWorkers) { | ||
let url; | ||
if (typeof Deno === "object") { | ||
// Deno can load the module regardless of MINE type. | ||
url = [ | ||
...((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('run.js', document.baseURI).href)).split("/").slice(0, -1)), | ||
"worker-web.mjs" | ||
].join("/"); | ||
if (entry) { | ||
url = entry; | ||
} | ||
else if (undefined) { | ||
// code is bundled, try the remote URL | ||
url = "https://ayonli.github.io/jsext/bundle/worker-web.mjs"; | ||
} | ||
else { | ||
url = [ | ||
...((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('run.js', document.baseURI).href)).split("/").slice(0, -1)), | ||
"worker-web.mjs" | ||
].join("/"); | ||
} | ||
} | ||
else { | ||
const _url = (options === null || options === void 0 ? void 0 : options.workerEntry) | ||
|| "https://ayonli.github.io/jsext/bundle/worker-web.mjs"; | ||
const _url = entry || "https://ayonli.github.io/jsext/bundle/worker-web.mjs"; | ||
const res = await fetch(_url); | ||
@@ -356,3 +344,3 @@ let blob; | ||
workerConsumerQueue.push(resolve); | ||
}).then(() => run(script, args, options)); | ||
}).then(() => run(_script, args, options)); | ||
} | ||
@@ -407,4 +395,11 @@ release = () => { | ||
} | ||
(function (run) { | ||
/** | ||
* The maximum number of workers allowed to exist at the same time. | ||
*/ | ||
run.maxWorkers = 16; | ||
})(run || (run = {})); | ||
var run$1 = run; | ||
exports.default = run; | ||
exports.default = run$1; | ||
//# sourceMappingURL=run.js.map |
if (!Symbol.asyncIterator) { | ||
// @ts-ignore | ||
Symbol.asyncIterator = Symbol("Symbol.asyncIterator"); | ||
@@ -7,3 +8,4 @@ } | ||
* Checks if the given object is an IteratorLike (implemented `next`). | ||
* @returns {obj is { next: Function }} | ||
* @param {any} obj | ||
* @returns {obj is { [x: string | symbol]: any; next: Function }} | ||
*/ | ||
@@ -21,2 +23,3 @@ function isIteratorLike(obj) { | ||
* `@@iterator` and `next`). | ||
* @param {any} obj | ||
*/ | ||
@@ -31,2 +34,3 @@ function isIterableIterator(obj) { | ||
* both `@@asyncIterator` and `next`). | ||
* @param {any} obj | ||
* @returns {obj is AsyncIterableIterator<any>} | ||
@@ -41,2 +45,3 @@ */ | ||
* Checks if the given object is a Generator. | ||
* @param {any} obj | ||
* @returns {obj is Generator} | ||
@@ -51,2 +56,3 @@ */ | ||
* Checks if the given object is an AsyncGenerator. | ||
* @param {any} obj | ||
* @returns {obj is AsyncGenerator} | ||
@@ -59,2 +65,5 @@ */ | ||
/** | ||
* @param {any} obj | ||
*/ | ||
function hasGeneratorSpecials(obj) { | ||
@@ -61,0 +70,0 @@ return typeof obj.return === "function" |
@@ -13,2 +13,3 @@ import _try from './try.js'; | ||
import example from './example.js'; | ||
import deprecate from './deprecate.js'; | ||
@@ -33,5 +34,6 @@ const AsyncFunction = (async function () { }).constructor; | ||
example, | ||
deprecate, | ||
}; | ||
export { AsyncFunction, AsyncGeneratorFunction, _try, chan, jsext as default, example, func, isSubclassOf, mixins, queue, read, readAll, run, throttle, wrap }; | ||
export { AsyncFunction, AsyncGeneratorFunction, _try, chan, jsext as default, deprecate, example, func, isSubclassOf, mixins, queue, read, readAll, run, throttle, wrap }; | ||
//# sourceMappingURL=index.js.map |
@@ -138,3 +138,3 @@ import { isValid } from '../object/index.js'; | ||
* | ||
* When parsing JSON via `parseAs`, this property is guaranteed to be of the given type. | ||
* When parsing JSON via {@link parseAs}, this property is guaranteed to be of the given type. | ||
* | ||
@@ -141,0 +141,0 @@ * NOTE: this decorator only supports TypeScript's `experimentalDecorators`. |
@@ -0,1 +1,5 @@ | ||
/** | ||
* Returns `true` if the specified object has the indicated property as its own property. | ||
* If the property is inherited, or does not exist, the function returns `false`. | ||
*/ | ||
function hasOwn(obj, key) { | ||
@@ -77,3 +81,3 @@ return Object.prototype.hasOwnProperty.call(obj, key); | ||
/** | ||
* Returns `true` if the given value is valid. Thee following values are considered invalid: | ||
* Returns `true` if the given value is valid. The following values are considered invalid: | ||
* | ||
@@ -80,0 +84,0 @@ * - `undefined` |
@@ -28,3 +28,3 @@ /** Try to resolve a promise with a timeout limit. */ | ||
} | ||
/** Blocks the context until the test is passed. */ | ||
/** Blocks the context until the test passes. */ | ||
async function until(test) { | ||
@@ -31,0 +31,0 @@ while ((await test()) === false) { |
@@ -34,3 +34,3 @@ import chan from './chan.js'; | ||
* Processes data sequentially by the given `handler` function and prevents concurrency | ||
* conflicts, it returns a queue instance that we can push data into. | ||
* conflicts, it returns a {@link Queue} instance that we can push data into. | ||
* | ||
@@ -37,0 +37,0 @@ * @param bufferSize The maximum capacity of the underlying channel, once reached, the push |
133
esm/run.js
import { sequence } from './number/index.js'; | ||
import { trim } from './string/index.js'; | ||
import chan from './chan.js'; | ||
import deprecate from './deprecate.js'; | ||
var _a; | ||
const isNode = typeof process === "object" && !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node); | ||
/** | ||
* The maximum number of workers allowed to exist at the same time. | ||
* | ||
* The primary purpose of the workers is not mean to run tasks in parallel, but run them in separate | ||
* from the main thread, so that aborting tasks can be achieved by terminating the worker thread and | ||
* it will not affect the main thread. | ||
* | ||
* That said, the worker thread can still be used to achieve parallelism, but it should be noticed | ||
* that only the numbers of tasks that equals to the CPU core numbers will be run at the same time. | ||
*/ | ||
const maxWorkerNum = 16; | ||
const workerIdCounter = sequence(1, Number.MAX_SAFE_INTEGER, 1, true); | ||
@@ -22,46 +13,23 @@ let workerPool = []; | ||
const workerConsumerQueue = []; | ||
/** | ||
* Runs a `script` in a worker thread or child process that can be aborted during runtime. | ||
* | ||
* In Node.js and Bun, the `script` can be either a CommonJS module or an ES module, and is relative | ||
* to the current working directory if not absolute. | ||
* | ||
* In browser and Deno, the `script` can only be an ES module, and is relative to the current URL | ||
* (or working directory for Deno) if not absolute. | ||
* | ||
* @example | ||
* ```ts | ||
* const job1 = await run("./job-example.mjs", ["World"]); | ||
* console.log(await job1.result()); // Hello, World | ||
* ``` | ||
* | ||
* @example | ||
* ```ts | ||
* const job2 = await run<string, [string[]]>("./job-example.mjs", [["foo", "bar"]], { | ||
* fn: "sequence", | ||
* }); | ||
* for await (const word of job2.iterate()) { | ||
* console.log(word); | ||
* } | ||
* // output: | ||
* // foo | ||
* // bar | ||
* ``` | ||
* | ||
* @example | ||
* ```ts | ||
* const job3 = await run<string, [string]>("./job-example.mjs", ["foobar"], { | ||
* fn: "takeTooLong", | ||
* }); | ||
* await job3.abort(); | ||
* const [err, res] = await _try(job3.result()); | ||
* console.assert(err === null); | ||
* console.assert(res === undefined); | ||
* ``` | ||
*/ | ||
async function run(script, args = undefined, options = undefined) { | ||
var _a, _b; | ||
let _script = ""; | ||
if (typeof script === "function") { | ||
let str = script.toString(); | ||
let start = str.lastIndexOf("("); | ||
if (start === -1) { | ||
throw new TypeError("the given script is not a dynamic import expression"); | ||
} | ||
else { | ||
start += 1; | ||
const end = str.indexOf(")", start); | ||
_script = trim(str.slice(start, end), ` '"\'`); | ||
} | ||
} | ||
else { | ||
_script = script; | ||
} | ||
const msg = { | ||
type: "ffi", | ||
script, | ||
script: _script, | ||
baseUrl: "", | ||
@@ -80,2 +48,6 @@ fn: (options === null || options === void 0 ? void 0 : options.fn) || "default", | ||
} | ||
if (options === null || options === void 0 ? void 0 : options.workerEntry) { | ||
deprecate("options.workerEntry", run, "set `run.workerEntry` instead"); | ||
} | ||
let entry = (options === null || options === void 0 ? void 0 : options.workerEntry) || run.workerEntry; | ||
let error = null; | ||
@@ -167,12 +139,20 @@ let result; | ||
if (isNode) { | ||
let entry = options === null || options === void 0 ? void 0 : options.workerEntry; | ||
if (!entry) { | ||
const path = await import('path'); | ||
const { fileURLToPath } = await import('url'); | ||
const dirname = path.dirname(fileURLToPath(import.meta.url)); | ||
if (["cjs", "esm"].includes(path.basename(dirname))) { // compiled | ||
entry = path.join(path.dirname(dirname), "bundle", "worker.mjs"); | ||
const _filename = fileURLToPath(import.meta.url); | ||
const _dirname = path.dirname(_filename); | ||
if (_filename === process.argv[1]) { | ||
// The code is bundled, try the worker entry in node_modules (if it exists). | ||
entry = "./node_modules/@ayonli/jsext/bundle/worker.mjs"; | ||
} | ||
else if ([ | ||
path.join("jsext", "cjs"), | ||
path.join("jsext", "esm"), | ||
path.join("jsext", "bundle") | ||
].some(path => _dirname.endsWith(path))) { // compiled | ||
entry = path.join(path.dirname(_dirname), "bundle", "worker.mjs"); | ||
} | ||
else { | ||
entry = path.join(dirname, "worker.mjs"); | ||
entry = path.join(_dirname, "worker.mjs"); | ||
} | ||
@@ -191,3 +171,3 @@ } | ||
} | ||
else if (workerPool.length < maxWorkerNum) { | ||
else if (workerPool.length < run.maxWorkers) { | ||
const { fork } = await import('child_process'); | ||
@@ -229,3 +209,3 @@ const isPrior14 = parseInt(process.version.slice(1)) < 14; | ||
workerConsumerQueue.push(resolve); | ||
}).then(() => run(script, args, options)); | ||
}).then(() => run(_script, args, options)); | ||
} | ||
@@ -256,3 +236,3 @@ release = () => { | ||
} | ||
else if (workerPool.length < maxWorkerNum) { | ||
else if (workerPool.length < run.maxWorkers) { | ||
const { Worker } = await import('worker_threads'); | ||
@@ -284,3 +264,3 @@ worker = new Worker(entry); | ||
workerConsumerQueue.push(resolve); | ||
}).then(() => run(script, args, options)); | ||
}).then(() => run(_script, args, options)); | ||
} | ||
@@ -311,14 +291,22 @@ release = () => { | ||
} | ||
else if (workerPool.length < maxWorkerNum) { | ||
else if (workerPool.length < run.maxWorkers) { | ||
let url; | ||
if (typeof Deno === "object") { | ||
// Deno can load the module regardless of MINE type. | ||
url = [ | ||
...(import.meta.url.split("/").slice(0, -1)), | ||
"worker-web.mjs" | ||
].join("/"); | ||
if (entry) { | ||
url = entry; | ||
} | ||
else if (import.meta["main"]) { | ||
// code is bundled, try the remote URL | ||
url = "https://ayonli.github.io/jsext/bundle/worker-web.mjs"; | ||
} | ||
else { | ||
url = [ | ||
...(import.meta.url.split("/").slice(0, -1)), | ||
"worker-web.mjs" | ||
].join("/"); | ||
} | ||
} | ||
else { | ||
const _url = (options === null || options === void 0 ? void 0 : options.workerEntry) | ||
|| "https://ayonli.github.io/jsext/bundle/worker-web.mjs"; | ||
const _url = entry || "https://ayonli.github.io/jsext/bundle/worker-web.mjs"; | ||
const res = await fetch(_url); | ||
@@ -349,3 +337,3 @@ let blob; | ||
workerConsumerQueue.push(resolve); | ||
}).then(() => run(script, args, options)); | ||
}).then(() => run(_script, args, options)); | ||
} | ||
@@ -400,4 +388,11 @@ release = () => { | ||
} | ||
(function (run) { | ||
/** | ||
* The maximum number of workers allowed to exist at the same time. | ||
*/ | ||
run.maxWorkers = 16; | ||
})(run || (run = {})); | ||
var run$1 = run; | ||
export { run as default }; | ||
export { run$1 as default }; | ||
//# sourceMappingURL=run.js.map |
@@ -11,2 +11,3 @@ import _try from "./try.ts"; | ||
import example from "./example.ts"; | ||
import deprecate from "./deprecate.ts"; | ||
@@ -61,2 +62,3 @@ export { Channel, Queue }; | ||
example, | ||
deprecate, | ||
}; | ||
@@ -78,2 +80,3 @@ | ||
example, | ||
deprecate, | ||
}; |
@@ -37,4 +37,5 @@ import { as, parseAs, type } from "./index.ts"; | ||
* | ||
* This function is primarily used in `parseAs` and shares the same conversion rules, but it | ||
* can be used in other scenarios too, for example, inside the `fromJSON` function. | ||
* This function is primarily used in {@link JSON.parseAs} and shares the same conversion | ||
* rules, but it can be used in other scenarios too, for example, inside the `fromJSON` | ||
* function. | ||
*/ | ||
@@ -49,3 +50,4 @@ as(data: unknown, type: StringConstructor): string | null; | ||
* | ||
* When parsing JSON via `JSON.parseAs`, this property is guaranteed to be of the given type. | ||
* When parsing JSON via {@link JSON.parseAs}, this property is guaranteed to be of the | ||
* given type. | ||
* | ||
@@ -52,0 +54,0 @@ * NOTE: this decorator only supports TypeScript's `experimentalDecorators`. |
@@ -47,3 +47,3 @@ import type { Constructor } from "../index.ts"; | ||
* | ||
* This function is primarily used in `parseAs` and shares the same conversion rules, but it | ||
* This function is primarily used in {@link parseAs} and shares the same conversion rules, but it | ||
* can be used in other scenarios too, for example, inside the `fromJSON` function. | ||
@@ -165,3 +165,3 @@ */ | ||
* | ||
* When parsing JSON via `parseAs`, this property is guaranteed to be of the given type. | ||
* When parsing JSON via {@link parseAs}, this property is guaranteed to be of the given type. | ||
* | ||
@@ -168,0 +168,0 @@ * NOTE: this decorator only supports TypeScript's `experimentalDecorators`. |
@@ -48,3 +48,3 @@ import { hasOwn, hasOwnMethod, omit, patch, pick, as, isValid } from "./index.ts"; | ||
/** | ||
* Returns `true` if the given value is valid. Thee following values are considered invalid: | ||
* Returns `true` if the given value is valid. The following values are considered invalid: | ||
* | ||
@@ -51,0 +51,0 @@ * - `undefined` |
import type { Constructor } from "../index.ts"; | ||
/** | ||
* Returns `true` if the specified object has the indicated property as its own property. | ||
* If the property is inherited, or does not exist, the function returns `false`. | ||
*/ | ||
export function hasOwn(obj: any, key: string | number | symbol): boolean { | ||
@@ -90,3 +94,3 @@ return Object.prototype.hasOwnProperty.call(obj, key); | ||
* ```ts | ||
* Object.as(bar, SomeType)?.doSomething(); | ||
* as(bar, SomeType)?.doSomething(); | ||
* ``` | ||
@@ -128,3 +132,3 @@ */ | ||
/** | ||
* Returns `true` if the given value is valid. Thee following values are considered invalid: | ||
* Returns `true` if the given value is valid. The following values are considered invalid: | ||
* | ||
@@ -131,0 +135,0 @@ * - `undefined` |
{ | ||
"name": "@ayonli/jsext", | ||
"version": "0.6.2", | ||
"version": "0.6.3", | ||
"description": "Additional functions for JavaScript programming in practice.", | ||
@@ -5,0 +5,0 @@ "main": "./cjs/index.js", |
@@ -11,3 +11,3 @@ import { after, sleep, timeout, until } from "./index.ts"; | ||
sleep(ms: number): Promise<void>; | ||
/** Blocks the context until the test is passed. */ | ||
/** Blocks the context until the test passes. */ | ||
until(test: () => boolean | Promise<boolean>): Promise<void>; | ||
@@ -14,0 +14,0 @@ } |
@@ -31,3 +31,3 @@ /** Try to resolve a promise with a timeout limit. */ | ||
/** Blocks the context until the test is passed. */ | ||
/** Blocks the context until the test passes. */ | ||
export async function until(test: () => boolean | Promise<boolean>): Promise<void> { | ||
@@ -34,0 +34,0 @@ while ((await test()) === false) { |
@@ -38,3 +38,3 @@ import chan, { Channel } from "./chan.ts"; | ||
* Processes data sequentially by the given `handler` function and prevents concurrency | ||
* conflicts, it returns a queue instance that we can push data into. | ||
* conflicts, it returns a {@link Queue} instance that we can push data into. | ||
* | ||
@@ -41,0 +41,0 @@ * @param bufferSize The maximum capacity of the underlying channel, once reached, the push |
172
README.md
@@ -49,2 +49,3 @@ # JsExt | ||
- [jsext.example](#jsextexample) | ||
- [jsext.deprecate](#jsextdeprecate) | ||
@@ -598,3 +599,3 @@ And other functions in [sub-packages](#sub-packages). | ||
```ts | ||
function run<T, A extends any[] = any[]>(script: string, args?: A, options?: { | ||
function run<R, A extends any[] = any[]>(script: string, args?: A, options?: { | ||
/** If not set, invoke the default function, otherwise invoke the specified function. */ | ||
@@ -616,3 +617,34 @@ fn?: string; | ||
adapter?: "worker_threads" | "child_process"; | ||
}): Promise<{ | ||
workerId: number; | ||
/** Terminates the worker and abort the task. */ | ||
abort(): Promise<void>; | ||
/** Retrieves the return value of the function that has been called.. */ | ||
result(): Promise<R>; | ||
/** Iterates the yield value if the function returns a generator. */ | ||
iterate(): AsyncIterable<R>; | ||
}>; | ||
function run<M extends { [x: string]: any; }, A extends Parameters<M[Fn]>, Fn extends keyof M = "default">( | ||
script: () => Promise<M>, | ||
args?: A, | ||
options?: { | ||
fn?: Fn; | ||
timeout?: number; | ||
keepAlive?: boolean; | ||
adapter?: "worker_threads" | "child_process"; | ||
} | ||
): Promise<{ | ||
workerId: number; | ||
abort(): Promise<void>; | ||
result(): Promise<ReturnType<M[Fn]> extends AsyncGenerator<any, infer R, any> ? R : Awaited<ReturnType<M[Fn]>>>; | ||
iterate(): AsyncIterable<ReturnType<M[Fn]> extends AsyncGenerator<infer Y, any, any> ? Y : Awaited<ReturnType<M[Fn]>>>; | ||
}>; | ||
namespace run { | ||
/** | ||
* The maximum number of workers allowed to exist at the same time. | ||
*/ | ||
export var maxWorkers = 16; | ||
/** | ||
* In browser, by default, the program loads the worker entry directly from GitHub, | ||
@@ -628,15 +660,7 @@ * which could be slow due to poor internet connection, we can copy the entry file | ||
*/ | ||
workerEntry?: string; | ||
}): Promise<{ | ||
workerId: number; | ||
/** Terminates the worker and abort the task. */ | ||
abort(): Promise<void>; | ||
/** Retrieves the return value of the function that has been called.. */ | ||
result(): Promise<T>; | ||
/** Iterates the yield value if the function returns a generator. */ | ||
iterate(): AsyncIterable<T>; | ||
}>; | ||
export var workerEntry: string | undefined; | ||
} | ||
``` | ||
Runs a `script` in a worker thread or child process that can be aborted during runtime. | ||
Runs the given `script` in a worker thread or child process for CPU-intensive or abortable tasks. | ||
@@ -649,6 +673,8 @@ In Node.js and Bun, the `script` can be either a CommonJS module or an ES module, and is relative to | ||
In Bun and Deno, the `script` can also be a TypeScript file. | ||
**Example (result)** | ||
```ts | ||
const job1 = await run("./job-example.mjs", ["World"]); | ||
const job1 = await run<string, [string]>("./job-example.mjs", ["World"]); | ||
console.log(await job1.result()); // Hello, World | ||
@@ -683,2 +709,9 @@ ``` | ||
**Example (import expression)** | ||
```ts | ||
const job4 = await run(() => import("./job-example.mjs"), ["World"]); | ||
console.log(await job4.result()); // Hello, World | ||
``` | ||
--- | ||
@@ -719,2 +752,54 @@ | ||
--- | ||
### jsext.deprecate | ||
```ts | ||
function deprecate<T, Fn extends (this: T, ...args: any[]) => any>( | ||
fn: Fn, | ||
tip?: string, | ||
once?: boolean | ||
): Fn; | ||
``` | ||
Marks a function as deprecated and returns a wrapped function. | ||
When the wrapped function is called, a deprecation warning will be emitted to the stdout. | ||
NOTE: the original function must have a name. | ||
**Example** | ||
```ts | ||
const sum = deprecate(function sum(a: number, b: number) { | ||
return a + b; | ||
}, "use `a + b` instead"); | ||
console.log(sum(1, 2)); | ||
// output: | ||
// DeprecationWarning: sum() is deprecated, use `a + b` instead (at <anonymous>:4:13) | ||
// 3 | ||
``` | ||
--- | ||
```ts | ||
function deprecate(target: string, forFn: Function, tip?: string, once?: boolean): void; | ||
``` | ||
Emits a deprecation warning for the target, usually a parameter, an option, or the function's name, | ||
etc. | ||
**Example** | ||
```ts | ||
const pow = function pow(a: number, b: number) { | ||
deprecate("pow()", pow, "use `a ** b` instead"); | ||
return a ** b; | ||
}; | ||
console.log(pow(2, 3)); | ||
// output: | ||
// DeprecationWarning: pow() is deprecated, use `a ** b` instead (at <anonymous>:5:13) | ||
// 8 | ||
``` | ||
## Types | ||
@@ -732,4 +817,4 @@ | ||
When [augment](./augment.ts)ing, these types will be exposed to the global scope (except for | ||
`Channel` and `Queue`). | ||
When [augment](https://github.com/ayonli/jsext/blob/main/augment.ts)ing, these types are exposed to | ||
the global scope (except for `Channel` and `Queue`). | ||
@@ -748,3 +833,3 @@ ## Sub-packages | ||
### [string](./string/index.ts) | ||
### [string](https://deno.land/x/ayonli_jsext/string/index.ts) | ||
@@ -772,3 +857,3 @@ ```js | ||
**[Augmentation](./string/augment.ts)** | ||
**[Augmentation](https://github.com/ayonli/jsext/blob/main/string/augment.ts)** | ||
@@ -790,3 +875,3 @@ - `String` | ||
### [number](./number/index.ts) | ||
### [number](https://deno.land/x/ayonli_jsext/number/index.ts) | ||
@@ -807,5 +892,6 @@ ```js | ||
*When [augment](./number/augment.ts)ing, these functions will be attached to the `Number` constructor.* | ||
*When [augment](https://github.com/ayonli/jsext/blob/main/number/augment.ts)ing, these functions* | ||
*are attached to the `Number` constructor.* | ||
### [array](./array/index.ts) | ||
### [array](https://deno.land/x/ayonli_jsext/array/index.ts) | ||
@@ -830,3 +916,3 @@ ```js | ||
**[Augmentation](./array/augment.ts)** | ||
**[Augmentation](https://github.com/ayonli/jsext/blob/main/array/augment.ts)** | ||
@@ -850,3 +936,3 @@ - `Array<T>` | ||
### [uint8array](/uint8array/index.ts) | ||
### [uint8array](https://deno.land/x/ayonli_jsext/uint8array/index.ts) | ||
@@ -866,3 +952,3 @@ ```js | ||
**[Augmentation](./uint8array/augment.ts)** | ||
**[Augmentation](https://github.com/ayonli/jsext/blob/main/uint8array/augment.ts)** | ||
@@ -876,3 +962,3 @@ - `Uint8Array` | ||
### [object](./object/index.ts) | ||
### [object](https://deno.land/x/ayonli_jsext/object/index.ts) | ||
@@ -905,5 +991,6 @@ ```js | ||
*When [augment](./object/augment.ts)ing, these functions will be attached to the `Object` constructor.* | ||
*When [augment](https://github.com/ayonli/jsext/blob/main/object/augment.ts)ing, these functions* | ||
*are attached to the `Object` constructor.* | ||
### [math](./math/index.ts) | ||
### [math](https://deno.land/x/ayonli_jsext/math/index.ts) | ||
@@ -922,5 +1009,6 @@ ```js | ||
*When [augment](./math/augment.ts)ing, these functions will be attached to the `Math` namespace.* | ||
*When [augment](https://github.com/ayonli/jsext/blob/main/math/augment.ts)ing, these functions* | ||
*are attached to the `Math` namespace.* | ||
### [promise](./promise/index.ts) | ||
### [promise](https://deno.land/x/ayonli_jsext/promise/index.ts) | ||
@@ -940,5 +1028,6 @@ ```js | ||
*When [augment](./promise/augment.ts)ing, these functions will be attached to the `Promise` constructor.* | ||
*When [augment](https://github.com/ayonli/jsext/blob/main/promise/augment.ts)ing, these functions* | ||
*are attached to the `Promise` constructor.* | ||
### [collections](./collections/index.ts) | ||
### [collections](https://deno.land/x/ayonli_jsext/collections/index.ts) | ||
@@ -965,5 +1054,6 @@ ```js | ||
*When [augment](./collections/augment.ts)ing, these types will be exposed to the global scope.* | ||
*When [augment](https://github.com/ayonli/jsext/blob/main/collections/augment.ts)ing, these types* | ||
*are exposed to the global scope.* | ||
### [error](./error/index.ts) | ||
### [error](https://deno.land/x/ayonli_jsext/error/index.ts) | ||
@@ -984,3 +1074,4 @@ ```js | ||
*When [augment](./error/augment.ts)ing, these types will be exposed to the global scope.* | ||
*When [augment](https://github.com/ayonli/jsext/blob/main/error/augment.ts)ing, these types* | ||
*are exposed to the global scope.* | ||
@@ -992,3 +1083,3 @@ **Functions** | ||
**[Augmentation](./error/augment.ts)** | ||
**[Augmentation](https://github.com/ayonli/jsext/blob/main/error/augment.ts)** | ||
@@ -1001,3 +1092,3 @@ - `Error` | ||
### [json](./json/index.ts) | ||
### [json](https://deno.land/x/ayonli_jsext/json/index.ts) | ||
@@ -1024,3 +1115,4 @@ ```ts | ||
*When [augment](./collections/augment.ts)ing, these functions will be attached to the `JSON` namespace.* | ||
*When [augment](https://github.com/ayonli/jsext/blob/main/json/augment.ts)ing, these functions* | ||
*are attached to the `JSON` namespace.* | ||
@@ -1035,6 +1127,6 @@ ## Import all sub-package augmentations at once | ||
If we're developing libraries and share them openly on NPM, in order to prevent collision, it's | ||
better not to use augmentations, but use the corresponding functions from the sub-packages instead. | ||
If we're developing libraries and share them publicly, in order to prevent collision, it's better | ||
not to use augmentations, but use the corresponding functions from the sub-packages instead. | ||
But if we're developing private projects, using augmentations can save a lot of time, it's easier to | ||
read and write, and make sense. | ||
But if we're developing private application, using augmentations can save a lot of time, it's easier | ||
to read and write, and make sense. |
180
run.ts
import type { Worker as NodeWorker } from "node:worker_threads"; | ||
import type { ChildProcess } from "node:child_process"; | ||
import { sequence } from "./number/index.ts"; | ||
import { trim } from "./string/index.ts"; | ||
import chan, { Channel } from "./chan.ts"; | ||
import deprecate from "./deprecate.ts"; | ||
@@ -9,14 +11,2 @@ const isNode = typeof process === "object" && !!process.versions?.node; | ||
/** | ||
* The maximum number of workers allowed to exist at the same time. | ||
* | ||
* The primary purpose of the workers is not mean to run tasks in parallel, but run them in separate | ||
* from the main thread, so that aborting tasks can be achieved by terminating the worker thread and | ||
* it will not affect the main thread. | ||
* | ||
* That said, the worker thread can still be used to achieve parallelism, but it should be noticed | ||
* that only the numbers of tasks that equals to the CPU core numbers will be run at the same time. | ||
*/ | ||
const maxWorkerNum = 16; | ||
const workerIdCounter = sequence(1, Number.MAX_SAFE_INTEGER, 1, true); | ||
@@ -35,3 +25,3 @@ let workerPool: { | ||
/** | ||
* Runs a `script` in a worker thread or child process that can be aborted during runtime. | ||
* Runs the given `script` in a worker thread or child process for CPU-intensive or abortable tasks. | ||
* | ||
@@ -44,2 +34,4 @@ * In Node.js and Bun, the `script` can be either a CommonJS module or an ES module, and is relative | ||
* | ||
* In Bun and Deno, the `script` can also be a TypeScript file. | ||
* | ||
* @example | ||
@@ -75,6 +67,6 @@ * ```ts | ||
*/ | ||
export default async function run<T, A extends any[] = any[]>( | ||
async function run<R, A extends any[] = any[]>( | ||
script: string, | ||
args: A | undefined = undefined, | ||
options: { | ||
args?: A, | ||
options?: { | ||
/** If not set, invoke the default function, otherwise invoke the specified function. */ | ||
@@ -97,13 +89,49 @@ fn?: string; | ||
/** | ||
* In browser, by default, the program loads the worker entry directly from GitHub, | ||
* which could be slow due to poor internet connection, we can copy the entry file | ||
* `bundle/worker-web.mjs` to a local path of our website and set this option to that path | ||
* so that it can be loaded locally. | ||
* | ||
* Or, if the code is bundled, the program won't be able to automatically locate the entry | ||
* file in the file system, in such case, we can also copy the entry file | ||
* (`bundle/worker.mjs` for Node.js and Bun, `bundle/worker-web.mjs` for browser and Deno) | ||
* to a local directory and supply this option instead. | ||
* @deprecated set `run.workerEntry` instead. | ||
*/ | ||
workerEntry?: string; | ||
} | ||
): Promise<{ | ||
workerId: number; | ||
/** Terminates the worker and abort the task. */ | ||
abort(): Promise<void>; | ||
/** Retrieves the return value of the function that has been called. */ | ||
result(): Promise<R>; | ||
/** Iterates the yield value if the function returns a generator. */ | ||
iterate(): AsyncIterable<R>; | ||
}>; | ||
/** | ||
* @param script A function containing a dynamic import expression, only used for TypeScript to | ||
* infer types in the given module, the module is never imported into the current program. | ||
* | ||
* @example | ||
* ```ts | ||
* const job4 = await run(() => import("./job-example.mjs"), ["World"]); | ||
* console.log(await job4.result()); // Hello, World | ||
* ``` | ||
*/ | ||
async function run<M extends { [x: string]: any; }, A extends Parameters<M[Fn]>, Fn extends keyof M = "default">( | ||
script: () => Promise<M>, | ||
args?: A, | ||
options?: { | ||
fn?: Fn; | ||
timeout?: number; | ||
keepAlive?: boolean; | ||
adapter?: "worker_threads" | "child_process"; | ||
} | ||
): Promise<{ | ||
workerId: number; | ||
abort(): Promise<void>; | ||
result(): Promise<ReturnType<M[Fn]> extends AsyncGenerator<any, infer R, any> ? R : Awaited<ReturnType<M[Fn]>>>; | ||
iterate(): AsyncIterable<ReturnType<M[Fn]> extends AsyncGenerator<infer Y, any, any> ? Y : Awaited<ReturnType<M[Fn]>>>; | ||
}>; | ||
async function run<R, A extends any[] = any[]>( | ||
script: string | (() => Promise<any>), | ||
args: A | undefined = undefined, | ||
options: { | ||
fn?: string; | ||
timeout?: number; | ||
keepAlive?: boolean; | ||
adapter?: "worker_threads" | "child_process"; | ||
workerEntry?: string; | ||
} | undefined = undefined | ||
@@ -115,9 +143,26 @@ ): Promise<{ | ||
/** Retrieves the return value of the function that has been called. */ | ||
result(): Promise<T>; | ||
result(): Promise<R>; | ||
/** Iterates the yield value if the function returns a generator. */ | ||
iterate(): AsyncIterable<T>; | ||
iterate(): AsyncIterable<R>; | ||
}> { | ||
let _script = ""; | ||
if (typeof script === "function") { | ||
let str = script.toString(); | ||
let start = str.lastIndexOf("("); | ||
if (start === -1) { | ||
throw new TypeError("the given script is not a dynamic import expression"); | ||
} else { | ||
start += 1; | ||
const end = str.indexOf(")", start); | ||
_script = trim(str.slice(start, end), ` '"\'`); | ||
} | ||
} else { | ||
_script = script; | ||
} | ||
const msg = { | ||
type: "ffi", | ||
script, | ||
script: _script, | ||
baseUrl: "", | ||
@@ -136,2 +181,7 @@ fn: options?.fn || "default", | ||
if (options?.workerEntry) { | ||
deprecate("options.workerEntry", run, "set `run.workerEntry` instead"); | ||
} | ||
let entry = options?.workerEntry || run.workerEntry; | ||
let error: Error | null = null; | ||
@@ -143,3 +193,3 @@ let result: { value: any; } | undefined; | ||
} | undefined; | ||
let channel: Channel<T> | undefined = undefined; | ||
let channel: Channel<R> | undefined = undefined; | ||
let workerId: number | undefined; | ||
@@ -224,13 +274,19 @@ let poolRecord: typeof workerPool[0] | undefined; | ||
if (isNode) { | ||
let entry = options?.workerEntry; | ||
if (!entry) { | ||
const path = await import("path"); | ||
const { fileURLToPath } = await import("url"); | ||
const dirname = path.dirname(fileURLToPath(import.meta.url)); | ||
const _filename = fileURLToPath(import.meta.url); | ||
const _dirname = path.dirname(_filename); | ||
if (["cjs", "esm"].includes(path.basename(dirname))) { // compiled | ||
entry = path.join(path.dirname(dirname), "bundle", "worker.mjs"); | ||
if (_filename === process.argv[1]) { | ||
// The code is bundled, try the worker entry in node_modules (if it exists). | ||
entry = "./node_modules/@ayonli/jsext/bundle/worker.mjs"; | ||
} else if ([ | ||
path.join("jsext", "cjs"), | ||
path.join("jsext", "esm"), | ||
path.join("jsext", "bundle") | ||
].some(path => _dirname.endsWith(path))) { // compiled | ||
entry = path.join(path.dirname(_dirname), "bundle", "worker.mjs"); | ||
} else { | ||
entry = path.join(dirname, "worker.mjs"); | ||
entry = path.join(_dirname, "worker.mjs"); | ||
} | ||
@@ -250,3 +306,3 @@ } | ||
poolRecord.busy = true; | ||
} else if (workerPool.length < maxWorkerNum) { | ||
} else if (workerPool.length < run.maxWorkers) { | ||
const { fork } = await import("child_process"); | ||
@@ -288,3 +344,3 @@ const isPrior14 = parseInt(process.version.slice(1)) < 14; | ||
workerConsumerQueue.push(resolve); | ||
}).then(() => run(script, args, options)); | ||
}).then(() => run(_script, args, options)); | ||
} | ||
@@ -316,3 +372,3 @@ | ||
poolRecord.busy = true; | ||
} else if (workerPool.length < maxWorkerNum) { | ||
} else if (workerPool.length < run.maxWorkers) { | ||
const { Worker } = await import("worker_threads"); | ||
@@ -343,3 +399,3 @@ worker = new Worker(entry); | ||
workerConsumerQueue.push(resolve); | ||
}).then(() => run(script, args, options)); | ||
}).then(() => run(_script, args, options)); | ||
} | ||
@@ -371,3 +427,3 @@ | ||
poolRecord.busy = true; | ||
} else if (workerPool.length < maxWorkerNum) { | ||
} else if (workerPool.length < run.maxWorkers) { | ||
let url: string; | ||
@@ -377,9 +433,15 @@ | ||
// Deno can load the module regardless of MINE type. | ||
url = [ | ||
...(import.meta.url.split("/").slice(0, -1)), | ||
"worker-web.mjs" | ||
].join("/"); | ||
if (entry) { | ||
url = entry; | ||
} else if ((import.meta as any)["main"]) { | ||
// code is bundled, try the remote URL | ||
url = "https://ayonli.github.io/jsext/bundle/worker-web.mjs"; | ||
} else { | ||
url = [ | ||
...(import.meta.url.split("/").slice(0, -1)), | ||
"worker-web.mjs" | ||
].join("/"); | ||
} | ||
} else { | ||
const _url = options?.workerEntry | ||
|| "https://ayonli.github.io/jsext/bundle/worker-web.mjs"; | ||
const _url = entry || "https://ayonli.github.io/jsext/bundle/worker-web.mjs"; | ||
const res = await fetch(_url); | ||
@@ -411,3 +473,3 @@ let blob: Blob; | ||
workerConsumerQueue.push(resolve); | ||
}).then(() => run(script, args, options)); | ||
}).then(() => run(_script, args, options)); | ||
} | ||
@@ -456,3 +518,3 @@ | ||
channel = chan<T>(Infinity); | ||
channel = chan<R>(Infinity); | ||
@@ -465,1 +527,23 @@ return { | ||
} | ||
namespace run { | ||
/** | ||
* The maximum number of workers allowed to exist at the same time. | ||
*/ | ||
export var maxWorkers = 16; | ||
/** | ||
* In browser, by default, the program loads the worker entry directly from GitHub, | ||
* which could be slow due to poor internet connection, we can copy the entry file | ||
* `bundle/worker-web.mjs` to a local path of our website and set this option to that path | ||
* so that it can be loaded locally. | ||
* | ||
* Or, if the code is bundled, the program won't be able to automatically locate the entry | ||
* file in the file system, in such case, we can also copy the entry file | ||
* (`bundle/worker.mjs` for Node.js and Bun, `bundle/worker-web.mjs` for browser and Deno) | ||
* to a local directory and supply this option instead. | ||
*/ | ||
export var workerEntry: string | undefined; | ||
} | ||
export default run; |
@@ -11,2 +11,3 @@ import _try from "./try.ts"; | ||
import example from "./example.ts"; | ||
import deprecate from "./deprecate.ts"; | ||
export { Channel, Queue }; | ||
@@ -54,3 +55,4 @@ export declare const AsyncFunction: AsyncFunctionConstructor; | ||
example: typeof example; | ||
deprecate: typeof deprecate; | ||
}; | ||
export { jsext as default, _try, func, wrap, throttle, mixins, isSubclassOf, chan, queue, read, readAll, run, example, }; | ||
export { jsext as default, _try, func, wrap, throttle, mixins, isSubclassOf, chan, queue, read, readAll, run, example, deprecate, }; |
@@ -37,4 +37,5 @@ declare global { | ||
* | ||
* This function is primarily used in `parseAs` and shares the same conversion rules, but it | ||
* can be used in other scenarios too, for example, inside the `fromJSON` function. | ||
* This function is primarily used in {@link JSON.parseAs} and shares the same conversion | ||
* rules, but it can be used in other scenarios too, for example, inside the `fromJSON` | ||
* function. | ||
*/ | ||
@@ -51,3 +52,4 @@ as(data: unknown, type: StringConstructor): string | null; | ||
* | ||
* When parsing JSON via `JSON.parseAs`, this property is guaranteed to be of the given type. | ||
* When parsing JSON via {@link JSON.parseAs}, this property is guaranteed to be of the | ||
* given type. | ||
* | ||
@@ -54,0 +56,0 @@ * NOTE: this decorator only supports TypeScript's `experimentalDecorators`. |
@@ -36,3 +36,3 @@ import type { Constructor } from "../index.ts"; | ||
* | ||
* This function is primarily used in `parseAs` and shares the same conversion rules, but it | ||
* This function is primarily used in {@link parseAs} and shares the same conversion rules, but it | ||
* can be used in other scenarios too, for example, inside the `fromJSON` function. | ||
@@ -50,3 +50,3 @@ */ | ||
* | ||
* When parsing JSON via `parseAs`, this property is guaranteed to be of the given type. | ||
* When parsing JSON via {@link parseAs}, this property is guaranteed to be of the given type. | ||
* | ||
@@ -53,0 +53,0 @@ * NOTE: this decorator only supports TypeScript's `experimentalDecorators`. |
@@ -46,3 +46,3 @@ declare global { | ||
/** | ||
* Returns `true` if the given value is valid. Thee following values are considered invalid: | ||
* Returns `true` if the given value is valid. The following values are considered invalid: | ||
* | ||
@@ -49,0 +49,0 @@ * - `undefined` |
import type { Constructor } from "../index.ts"; | ||
/** | ||
* Returns `true` if the specified object has the indicated property as its own property. | ||
* If the property is inherited, or does not exist, the function returns `false`. | ||
*/ | ||
export declare function hasOwn(obj: any, key: string | number | symbol): boolean; | ||
@@ -35,3 +39,3 @@ /** | ||
* ```ts | ||
* Object.as(bar, SomeType)?.doSomething(); | ||
* as(bar, SomeType)?.doSomething(); | ||
* ``` | ||
@@ -46,3 +50,3 @@ */ | ||
/** | ||
* Returns `true` if the given value is valid. Thee following values are considered invalid: | ||
* Returns `true` if the given value is valid. The following values are considered invalid: | ||
* | ||
@@ -49,0 +53,0 @@ * - `undefined` |
@@ -9,3 +9,3 @@ declare global { | ||
sleep(ms: number): Promise<void>; | ||
/** Blocks the context until the test is passed. */ | ||
/** Blocks the context until the test passes. */ | ||
until(test: () => boolean | Promise<boolean>): Promise<void>; | ||
@@ -12,0 +12,0 @@ } |
@@ -7,3 +7,3 @@ /** Try to resolve a promise with a timeout limit. */ | ||
export declare function sleep(ms: number): Promise<void>; | ||
/** Blocks the context until the test is passed. */ | ||
/** Blocks the context until the test passes. */ | ||
export declare function until(test: () => boolean | Promise<boolean>): Promise<void>; |
@@ -11,3 +11,3 @@ export declare class Queue<T> { | ||
* Processes data sequentially by the given `handler` function and prevents concurrency | ||
* conflicts, it returns a queue instance that we can push data into. | ||
* conflicts, it returns a {@link Queue} instance that we can push data into. | ||
* | ||
@@ -14,0 +14,0 @@ * @param bufferSize The maximum capacity of the underlying channel, once reached, the push |
/** | ||
* Runs a `script` in a worker thread or child process that can be aborted during runtime. | ||
* Runs the given `script` in a worker thread or child process for CPU-intensive or abortable tasks. | ||
* | ||
@@ -10,2 +10,4 @@ * In Node.js and Bun, the `script` can be either a CommonJS module or an ES module, and is relative | ||
* | ||
* In Bun and Deno, the `script` can also be a TypeScript file. | ||
* | ||
* @example | ||
@@ -41,3 +43,3 @@ * ```ts | ||
*/ | ||
export default function run<T, A extends any[] = any[]>(script: string, args?: A | undefined, options?: { | ||
declare function run<R, A extends any[] = any[]>(script: string, args?: A, options?: { | ||
/** If not set, invoke the default function, otherwise invoke the specified function. */ | ||
@@ -60,2 +62,43 @@ fn?: string; | ||
/** | ||
* @deprecated set `run.workerEntry` instead. | ||
*/ | ||
workerEntry?: string; | ||
}): Promise<{ | ||
workerId: number; | ||
/** Terminates the worker and abort the task. */ | ||
abort(): Promise<void>; | ||
/** Retrieves the return value of the function that has been called. */ | ||
result(): Promise<R>; | ||
/** Iterates the yield value if the function returns a generator. */ | ||
iterate(): AsyncIterable<R>; | ||
}>; | ||
/** | ||
* @param script A function containing a dynamic import expression, only used for TypeScript to | ||
* infer types in the given module, the module is never imported into the current program. | ||
* | ||
* @example | ||
* ```ts | ||
* const job4 = await run(() => import("./job-example.mjs"), ["World"]); | ||
* console.log(await job4.result()); // Hello, World | ||
* ``` | ||
*/ | ||
declare function run<M extends { | ||
[x: string]: any; | ||
}, A extends Parameters<M[Fn]>, Fn extends keyof M = "default">(script: () => Promise<M>, args?: A, options?: { | ||
fn?: Fn; | ||
timeout?: number; | ||
keepAlive?: boolean; | ||
adapter?: "worker_threads" | "child_process"; | ||
}): Promise<{ | ||
workerId: number; | ||
abort(): Promise<void>; | ||
result(): Promise<ReturnType<M[Fn]> extends AsyncGenerator<any, infer R, any> ? R : Awaited<ReturnType<M[Fn]>>>; | ||
iterate(): AsyncIterable<ReturnType<M[Fn]> extends AsyncGenerator<infer Y, any, any> ? Y : Awaited<ReturnType<M[Fn]>>>; | ||
}>; | ||
declare namespace run { | ||
/** | ||
* The maximum number of workers allowed to exist at the same time. | ||
*/ | ||
var maxWorkers: number; | ||
/** | ||
* In browser, by default, the program loads the worker entry directly from GitHub, | ||
@@ -71,11 +114,4 @@ * which could be slow due to poor internet connection, we can copy the entry file | ||
*/ | ||
workerEntry?: string; | ||
} | undefined): Promise<{ | ||
workerId: number; | ||
/** Terminates the worker and abort the task. */ | ||
abort(): Promise<void>; | ||
/** Retrieves the return value of the function that has been called. */ | ||
result(): Promise<T>; | ||
/** Iterates the yield value if the function returns a generator. */ | ||
iterate(): AsyncIterable<T>; | ||
}>; | ||
var workerEntry: string | undefined; | ||
} | ||
export default run; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
744908
232
10372
1108