diff --git a/.eslintrc b/.eslintrc index ed994f3a1..b6f3fb362 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,4 +1,8 @@ { + "parserOptions": { + "projects": ["./server.tsconfig.json"] + }, + "extends": [ "./node_modules/@v4fire/linters/.eslintrc" ] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..81eb5e9d5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,52 @@ +name: Release to npm + +on: + release: + types: ["published"] + workflow_dispatch: + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "20.x" + registry-url: "https://registry.npmjs.org" + cache: "yarn" + + - run: yarn install + + - name: Build standalone version + run: yarn build:standalone + + - uses: actions/github-script@v7 + name: Extract tag + id: extract-tag + with: + result-encoding: string + script: | + const {version} = require('${{ github.workspace }}/package.json'); + const semver = require('semver'); + + const DEFAULT_TAG = 'latest'; + const parsedVersion = semver.parse(version); + + if (parsedVersion == null) { + return DEFAULT_TAG; + } + + const {prerelease} = parsedVersion; + + if (prerelease.length === 0) { + return DEFAULT_TAG; + } + + const tag = prerelease[0]; + return typeof tag === 'string' ? tag : DEFAULT_TAG; + + - run: npm publish --access public --tag "${{ steps.extract-tag.outputs.result }}" + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 834e4d8c0..0ff6239f3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,10 +2,10 @@ name: build on: push: - branches: [master] + branches: [master, v4, v4-rc] pull_request: - branches: [master] + branches: [master, v4, v4-rc] jobs: linters: @@ -13,7 +13,7 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [18.x] steps: - uses: actions/checkout@v3 @@ -38,7 +38,7 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [18.x] steps: - uses: actions/checkout@v3 @@ -68,7 +68,7 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [18.x] steps: - uses: actions/checkout@v3 @@ -93,7 +93,7 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [18.x] steps: - uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index c61d11ac7..4f506c8b0 100644 --- a/.gitignore +++ b/.gitignore @@ -35,9 +35,6 @@ $RECYCLE.BIN !.yarn/sdks !.yarn/versions -# Icon must ends with two \r. -Icon - # Thumbnails ._* @@ -53,6 +50,7 @@ Icon # Build && test /dist +/docs /coverage /.nyc_output diff --git a/.npmignore b/.npmignore index cb32b1cec..27ef78842 100644 --- a/.npmignore +++ b/.npmignore @@ -27,9 +27,6 @@ $RECYCLE.BIN .LSOverride .vscode -# Icon must ends with two \r. -Icon - # Thumbnails ._* @@ -69,4 +66,5 @@ Icon /docs /.github /assets -/typedoc.json \ No newline at end of file +/typedoc.json + diff --git a/.tsconfig b/.tsconfig index c6f97263b..5875fb478 100644 --- a/.tsconfig +++ b/.tsconfig @@ -1,21 +1,43 @@ { "compilerOptions": { "baseUrl": ".", + "outDir": "ts_dist", + "target": "es5", "module": "commonjs", - "downlevelIteration": true, + "esModuleInterop": true, - "importHelpers": true, - "importsNotUsedAsValues": "error", + "downlevelIteration": true, + "lib": ["dom", "esnext"], - "outDir": "ts_dist", - "forceConsistentCasingInFileNames": true, - "disableSizeLimit": true, + "experimentalDecorators": true, + "importHelpers": true, + + "allowJs": false, "skipLibCheck": false, + + "disableSizeLimit": true, + "forceConsistentCasingInFileNames": true, + "strictNullChecks": true, "strictFunctionTypes": true, "strictPropertyInitialization": true, - "experimentalDecorators": true, - "noImplicitOverride": true - } + + "noImplicitOverride": true, + "importsNotUsedAsValues": "error", + + "isolatedModules": false + }, + + "include": [ + "./src/**/*.ts", + "./src/**/*.js", + "./config/**/*.js", + "./build/**/*.js" + ], + + "files": [ + "./index.d.ts", + "./jest.config.ts" + ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 61ba3b481..142680371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,361 @@ Changelog _Note: Gaps between patch versions are faulty, broken or test releases._ +## v4.0.0-alpha.54 (2024-12-25) + +#### :boom: Breaking Change + +* Renamed `once` method in Function prototype to `memoize` `core/prelude/function/memoize` +* Renamed `once` decorator to `memoize` `core/functools/memoize` +* Removed deprecated module `core/decorators` + +## v4.0.0-alpha.53 (2024-12-16) + +#### :bug: Bug Fix + +* Added handling the rejection of provider in provider request engine `core/request/engines/provider` + +## v4.0.0-alpha.52 (2024-12-12) + +#### :house: Internal + +* `target` option for `ts-node` set to `esnext` + +## v4.0.0-alpha.51 (2024-12-02) + +#### :rocket: New Feature + +* Added support for propagating status code and headers from exactly one request of the composition +via `propagateStatusAndHeaders` option `core/request/engines/composition` +* Added support for `redirect` option `core/request/engines/fetch` + +## v4.0.0-alpha.50 (2024-11-26) + +#### :rocket: New Feature + +* `stderr` can now accept error details and pass them to the logger `core/prelude/global` + +## v4.0.0-alpha.49 (2024-10-31) + +#### :bug: Bug Fix + +* `core/prelude/i18n/helpers` + * Fix logging bug in `pluralizeText`. + * Add logging info in i18n helpers. + +## v4.0.0-alpha.48 (2024-10-30) + +#### :bug: Bug Fix + +* Fixed an issue when receiving an empty string with the `Content-Type: application/octet-stream` header `core/request/response` + +## v4.0.0-alpha.47.speedup (2024-10-01) + +#### :boom: Breaking Change + +* `core/async`: + * Moved code from `V4/Client` + * Removed deprecated API and refactored internal structures + +#### :rocket: New Feature + +* Added a new method `Array.toArray` `core/prelude` +* Added support for baked symbols `core/symbol` + +#### :house: Internal + +* Conduct refactoring and optimization of the module `core/async` +* Various performance optimizations + +## v4.0.0-alpha.46 (2024-09-25) + +#### :boom: breaking change + +* `core/prelude/i18n/helpers` + * changed `i18n` translations format. + * added `intl` support for pluralization. + * now `i18n` prefer to use `intl` api for pluralization if it's possible, otherwise fallback to old plural form logic. + +## v4.0.0-alpha.45 (2024-09-09) + +#### :bug: Bug Fix + +* Fixed an issue with the calculation of timezones without considering time `core/prelude/date/create` + +## v4.0.0-alpha.44 (2024-09-06) + +#### :bug: Bug Fix + +* Avoid reassigning an `undefined` value for the scheme from the prototype `core/lazy` + +## v4.0.0-alpha.43 (2024-07-29) + +#### :house: Internal + +* RC branch merge + +## v4.0.0-alpha.42 (2024-07-29) + +#### :bug: Bug Fix + +* Fixed memory leak with singleton providers cache `core/data/modules` + +## v4.0.0-alpha.40 (2024-07-02) + +#### :bug: Bug Fix + +* Fixed an incorrect import from `core/status-codes`. `core/request/engines/composition` + +## v4.0.0-alpha.39 (2024-07-01) + +#### :rocket: New Feature + +* Added a new engine for the request module - `compositionEngine`. `compositionEngine` allows you to solve problems +composition of requests in more complex structures (such as Provider). `core/request/engines/composition` + +## v4.0.0-alpha.38 (2024-06-28) + +#### :rocket: New Feature + +* Added a new option `noContentStatuses`. This option allows to pass custom status code, array or range of status codes + which indicate a no-content response. By default, an array `[...Range(100, 199), 204, 304]` is used, but it may be useful + to override this value if your backend uses different status codes for no-content responses. `core/request` + +## v4.0.0-alpha.37 (2024-06-03) + +#### :bug: Bug Fix + +* Fixed an issue with clearing `nextTick` `core/async` + +## v4.0.0-alpha.36 (2024-05-16) + +#### :bug: Bug Fix + +* Fixed an issue where an internal property, blackList, was being written to the target object `core/object/watch` + +## v4.0.0-alpha.35 (2024-05-13) + +#### :bug: Bug Fix + +* Added logic that checks whether the passed instance is of the native `Headers` class or `V4Headers`. + To avoid overwriting response header values, we use the `append` method when handling the native Headers object + received from the response, while retaining the original practice of setting headers with the `set` method when + constructing request headers `core/request/headers` + +## v4.0.0-alpha.34 (2024-05-06) + +#### :rocket: New Feature + +* Added support for specifying a region for i18n `core/prelude` + +## v4.0.0-alpha.33 (2024-04-19) + +#### :bug: Bug Fix + +* Now, the custom `Headers` class uses the `append` method + to construct a new set of headers based on the built-in Headers in the Fetch API implementation `core/request/headers` + +## v4.0.0-alpha.32 (2024-04-16) + +#### :bug: Bug Fix + +* Fixed type generation for standalone build + +## v4.0.0-alpha.31 (2024-04-12) + +#### :house: Internal + +* Use `WeakRef` for `RequestError` details only in node environment `core/request/error` + +## v4.0.0-alpha.30 (2024-04-09) + +#### :boom: Breaking Change + +* Support for the `Got` library based query engine has been removed. + Now, built-in fetch is used with Node.js. `core/request` + +#### :rocket: New Feature + +* Now `MiddlewareParams` are being passed to the `RequestAPIValue` function `core/request` + +## v4.0.0-alpha.29 (2024-04-04) + +#### :boom: Breaking Change + +* Dropped API support with rest parameters in the `addToPrototype` `core/prelude/function/extend`. + Migration Guide: `Foo.addToPrototype(bar, baz)` -> `Foo.addToPrototype({bar, baz})`. + +#### :bug: Bug Fix + +* Fixed `providerOptions` inheritance by ExtraProviders `core/data` +* Fixed the bug with the `debounce` decorator: + conflicts between debounced methods across instances of identical classes or components have been resolved `core/functools` + +## v4.0.0-alpha.28 (2024-03-21) + +#### :bug: Bug Fix + +* Revert `v4.0.0-alpha.27` + +## v4.0.0-alpha.27 (2024-03-21) + +#### :bug: Bug Fix + +* Fixed a memory leak `core/lazy` + +## v4.0.0-alpha.26 (2024-03-21) + +#### :rocket: New Feature + + * The standalone version of a library now has `.d.ts` declaration files + +## v4.0.0-alpha.25 (2024-03-20) + +#### :bug: Bug Fix + +* Fixed a memory leak `core/data` + +## v4.0.0-alpha.24 (2024-03-12) + +#### :bug: Bug Fix + +* The error that caused the application process to never finish during SSR has been fixed `core/net` + +## v4.0.0-alpha.23 (2024-03-01) + +#### :bug: Bug Fix + +* Return event object from the `on` method of the wrapped emitter `core/async/modules/wrappers` + +## v4.0.0-alpha.22 (2024-02-27) + +#### :rocket: New Feature + +* Added a `recursive` parameter to the `dropCache` method for recursive cache clearing to prevent memory leaks during SSR: + * `core/data` + * `core/request` + +## v4.0.0-alpha.21 (2024-02-12) + +#### :rocket: New Feature + +* Added a method to destroy the created provider `core/data` + +## v4.0.0-alpha.20 (2024-02-06) + +#### :boom: Breaking Change + +* `core/lazy` no longer has a default export `core/lazy` + +#### :bug: Bug Fix + +* Added the `disposeLazy` function which clears the `makeLazy` module of the passed context to avoid memory leaks `core/lazy` + +## v4.0.0-alpha.19 (2024-01-12) + +#### :bug: Bug Fix + +* Quickfix for `v4.0.0-alpha.18` + +## v4.0.0-alpha.18 (2024-01-12) + +#### :bug: Bug Fix + +* Fixed memory leak when using wrappers over event emitters `core/async` + +## v4.0.0-alpha.17 (2024-01-11) + +#### :rocket: New Feature + +* Added `Object.isEmpty` + +## v4.0.0-alpha.16 (2023-12-19) + +#### :bug: Bug Fix + +* Fixed cache initialization for providers + +## v4.0.0-alpha.15 (2023-11-26) + +#### :rocket: New Feature + +* Added a constant to determine if SSR is enabled `core/env` + +#### :bug: Bug Fix + +* Do not check online during SSR `core/net` + +## v4.0.0-alpha.14 (2023-11-22) + +#### :house: Internal + +* Fixed TS types of the EventEmitter wrapper `core/async` + +## v4.0.0-alpha.13 (2023-11-16) + +#### :boom: Breaking Change + +* Removed the `i18n` function from the global scope + +## v4.0.0-alpha.12 (2023-10-17) + +#### :rocket: New Feature + +* `core/data`: + * Added a new option `singleton` for data providers + * Caching of requests during SSR is allowed + +#### :bug: Bug Fix + +* By default, the getCacheKey function should take the parameters from the provider `core/data` + +## v4.0.0-alpha.11 (2023-10-12) + +#### :rocket: New Feature + +* Now parameters from the main provider are being passed to the engine based on providers `core/request` + +#### :bug: Bug Fix + +* Caching should be disabled in SSR `core/request` + +## v4.0.0-alpha.10 (2023-09-29) + +#### :nail_care: Polish + +* Enhanced error message in `prelude/i18n` for missing translations + +## v4.0.0-alpha.9 (2023-09-19) + +#### :rocket: New Feature + +* Added API for serializing custom objects `core/json` + +## v4.0.0-alpha.8 (2023-09-15) + +#### :rocket: New Feature + +* Added a new property `params` `core/data` + +## v4.0.0-alpha.6 (2023-07-11) + +#### :boom: Breaking Change + +* Removed `upd` and `del` methods +* Renamed provider events `upd/del` to `update/delete` + +#### :rocket: New Feature + +* Added result hashing for `Object.fastHash` `core/prelude` +* Added an engine that allows storing data in a single string `core/kv-storage` + +#### :house: Internal + +* Updated global object definition to be universal for any JS environment `core/prelude/env/const` + +#### :bug: Bug Fix + +* Fixed an error when calling `String.capitalize` with an empty string ## v3.95.4 (2023-05-25) diff --git a/build/CHANGELOG.md b/build/CHANGELOG.md index 896735148..238395339 100644 --- a/build/CHANGELOG.md +++ b/build/CHANGELOG.md @@ -9,6 +9,12 @@ Changelog > - :house: [Internal] > - :nail_care: [Polish] +## v4.0.0-alpha.52 (2024-12-12) + +#### :house: Internal + +* `target` option for `ts-node` set to `esnext` + ## v3.86.2 (2022-05-18) #### :bug: Bug Fix diff --git a/build/README.md b/build/README.md index c05e85cb0..32b5b44e3 100644 --- a/build/README.md +++ b/build/README.md @@ -36,7 +36,7 @@ You should use this function in child projects. 'use strict'; const - config = require('config'); + config = require('@config/config'); module.exports = function (gulp = require('gulp')) { include('@super/core/gulpfile', __dirname)(gulp); diff --git a/build/build.gulp.js b/build/build.gulp.js index f8fea1cea..9de3119e1 100644 --- a/build/build.gulp.js +++ b/build/build.gulp.js @@ -67,6 +67,12 @@ module.exports = function init(gulp) { gulp.task('build:standalone', gulp.series([ gulp.parallel(['build:tsconfig', 'clean:standalone']), buildStandalone, + buildDeclStandalone, + replaceTscAliases + ])); + + gulp.task('build:standalone-decl', gulp.series([ + buildDeclStandalone, replaceTscAliases ])); @@ -109,41 +115,19 @@ module.exports = function init(gulp) { opts = {type: 'server', ...opts}; const - isPathInside = require('is-path-inside'); - - const - tsConfig = fs.readJSONSync(src.rel(`./${opts.type}.tsconfig.json`)); - - filesToBuild = [ - ...tsConfig.include || [], - ...resolve.rootDependencies.map((el) => `${el}/**/*.@(ts|js)`) - ]; - - const - isDep = new RegExp(`(^.*?(?:^|[\\/])(${depsRgxpStr}))(?:$|[\\/])`), + tsConfig = fs.readJSONSync(src.rel(`./${opts.type}.tsconfig.json`)), enableSourcemaps = process.env.SOURCEMAPS; const requireInitializer = `/* istanbul ignore next */(${h.redefineRequire.toString()})();\n`, insertRequireInitializer = h.prependCode(requireInitializer); - function dest(file) { - const - depDecl = isDep.exec(file.path); - - if (depDecl) { - file.base = resolve.rootDependencies - .find((el) => isPathInside(fs.realpathSync(el), fs.realpathSync(depDecl[1]))); - - return src.lib(depDecl[2]); - } - - file.base = src.src(); - return src[`${opts.type}Output`](); - } - return gulp - .src(filesToBuild, {base: './', since: gulp.lastRun(`build:${opts.type}`)}) + .src(getFilesToBuild(tsConfig), { + base: './', + since: gulp.lastRun(`build:${opts.type}`) + }) + .pipe($.if(enableSourcemaps, $.sourcemaps.init())) .pipe($.plumber()) @@ -168,13 +152,58 @@ module.exports = function init(gulp) { ) .pipe($.if(enableSourcemaps, $.sourcemaps.write('.'))) - .pipe(gulp.dest(dest)); + .pipe(gulp.dest((file) => dest(file, opts))); } function buildStandalone() { return baseBuild({type: 'standalone'}); } + function buildDeclStandalone(opts) { + opts = {type: 'standalone', ...opts}; + + const + tsConfig = fs.readJSONSync(src.rel(`./${opts.type}.tsconfig.json`)), + tsProject = $.typescript.createProject(`${opts.type}.tsconfig.json`); + + const tsResult = gulp + .src(getFilesToBuild(tsConfig)) + .pipe($.plumber()) + .pipe(tsProject()); + + return tsResult.dts.pipe(gulp.dest('lib')); + } + + function dest(file, opts) { + const + isPathInside = require('is-path-inside'); + + const + isDep = new RegExp(`(^.*?(?:^|[\\/])(${depsRgxpStr}))(?:$|[\\/])`), + depDecl = isDep.exec(file.path); + + if (depDecl) { + file.base = resolve.rootDependencies + .find((el) => isPathInside(fs.realpathSync(el), fs.realpathSync(depDecl[1]))); + + return src.lib(depDecl[2]); + } + + file.base = src.src(); + return src[`${opts.type}Output`](); + } + + function getFilesToBuild(tsConfig) { + filesToBuild = [ + ...tsConfig.include || [], + ...tsConfig.files || [], + ...resolve.rootDependencies.map((el) => `${el}/**/*.@(ts|js)`), + '!src/**/*.spec.js' + ]; + + return filesToBuild; + } + function replaceTscAliases() { return replaceTscAliasPaths({ configFile: src.rel('./standalone.tsconfig.json'), diff --git a/build/include.js b/build/include.js index e5cab6e58..d15ed1393 100644 --- a/build/include.js +++ b/build/include.js @@ -28,23 +28,24 @@ const const IncludeOptions = {}; /** - * Returns a function to attach modules within node.js process with the support of WebPack "layers". - * The returned function takes a path to require and takes a caller file's directory (to enable "@super" alias). - * Also, the function can return a path or source code of the found file if there is provided special flag. + * Returns a function, which can import modules within node.js process from the specified directories. + * The returned function takes a path to require and + * optionally takes a caller file's directory (to enable "@super" alias). + * Also, the function can return a path or source code of the found file if a special flag is provided. * * @param {Array} layers - list of layers * @returns {function (string, (string|IncludeOptions)?): ?} * * @example * ```js - * global.include = include(['./', './node_modules/bla']); + * global.include = include(['./node_modules/bla', './']); * - * // Searches the first existed file from a list: + * // Searches the first existing file from a list: * // ./build/i18n * // ./node_modules/bla/build/i18n * include('build/i18n'); * - * // Searches the first existed file from a list: + * // Searches the first existing file from a list: * // ./node_modules/bla/build/i18n * include('@super/build/i18n', __dirname); * @@ -54,7 +55,7 @@ const IncludeOptions = {}; * // Returns a path to the file * include('build/i18n', {return: 'path'); * - * // Returns source code of the file + * // Returns the source code of the file * include('@super/build/i18n', {return: 'source'); * ``` */ diff --git a/build/tsnode.js b/build/tsnode.js index 745f83f28..2f61bffe2 100644 --- a/build/tsnode.js +++ b/build/tsnode.js @@ -72,7 +72,7 @@ module.exports = function initTsNode() { compilerOptions: { module: 'commonjs', - target: 'es2021' + target: 'esnext' }, ignore: [], diff --git a/config/default.js b/config/default.js index 2141f1af3..0635861ac 100644 --- a/config/default.js +++ b/config/default.js @@ -74,6 +74,22 @@ module.exports = config.createConfig( } }), + /** + * Default application region + * + * @cli region + * @env REGION + * + * @type {string} + */ + region: o('region', { + env: true, + coerce(value) { + globalThis['REGION'] = value; + return value; + } + }), + /** * Application environment (prod, stage, etc.) * diff --git a/index.d.ts b/index.d.ts index 3635c1b01..4aade1ad0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -6,4115 +6,11 @@ * https://github.com/V4Fire/Core/blob/master/LICENSE */ -/* eslint-disable max-lines, @typescript-eslint/unified-signatures */ - /** * [[include:core/prelude/README.md]] * @packageDocumentation */ -declare namespace TB { - type Cast = X extends Y ? X : Y; - - type Type = A & { - [K in 'symbol']: ID; - }; - - type Length = T['length']; - - type Head = T extends [infer H, ...any[]] ? - H : never; - - type Tail = T extends [any, ...infer TT] ? - TT : []; - - type HasTail = Length extends 0 | 1 ? - false : true; - - type Last = HasTail extends true ? - Last> : Head; - - type Prepend = [E, ...T]; - - type Drop = Length extends N ? - T : Drop, Prepend>; - - type Pos = Length; - - type Next = Prepend; - type Prev = Tail; - - type Reverse = Pos extends Length ? - R : Reverse], R>, Next>; - - type Concat = Reverse, any[]>, T2>; - - type Append = Concat; - - type __ = Type<{}, 'x'>; - - type GapOf = - T1[Pos] extends __ ? Append], TN> : TN; - - type GapsOf = - Pos extends Length ? - Concat, T2>, any[]>> : - GapsOf, any[]>, Next>; - - type PartialGaps = { - [K in keyof T]?: T[K] | __; - }; - - type CleanedGaps = { - [K in keyof T]: NonNullable; - }; - - type Gaps = CleanedGaps> extends [...infer A] ? - A : never; - - type Curry any)> = - (...args: Cast>>) => - GapsOf> extends [any, ...any[]] ? - Curry<(...args: Cast>, any[]>) => ReturnType> : - ReturnType; -} - -declare const DEBUG: boolean; -declare const IS_PROD: boolean; - -declare const APP_NAME: string; -declare const API_URL: CanUndef; -declare const LOCALE: Language; - -type Language = - 'be' | 'en' | 'kk' | - 'ru' | 'tr' | 'tt' | - 'uk' | 'id' | 'uz' | - 'es' | 'de' | 'hy' | - 'ka' | 'ky' | 'sr' | - 'fr' | 'lv' | 'lt' | - 'ro' | 'fi' | 'az' | - 'zh' | 'he' | 'et' | - 'no' | 'sv' | 'pt' | - 'ar' | 'sw'; - -/** - * Converts the specified unknown value to any - * @param obj - */ -declare function Any(obj: any): any; - -/** - * STDERR wrapper - * @param err - */ -declare function stderr(err: any): void; - -/** - * Creates a function to internationalize strings in an application based on the given locale and keyset. - * Keyset allows you to share the same keys in different contexts. - * For example, the key "Next" may have a different value in different components of the application, therefore, - * we can use the name of the component as a keyset value. - * - * @param keysetNameOrNames - the name of keyset or array with names of keysets to use. - * If passed as an array, the priority of the cases will be arranged in the order of the elements, - * the first one will have the highest priority. - * - * @param [customLocale] - the locale used to search for translations (the default is taken from - * the application settings) - */ -declare function i18n( - keysetNameOrNames: CanArray, customLocale?: Language -): (key: string | TemplateStringsArray, params?: I18nParams) => string; - -/** - * Parameters for the internationalization function - */ -type I18nParams = {count?: number | StringPluralizationForms} & { - [key: string]: string | number; -}; - -/** - * String pluralization constants that can be used instead of numbers - */ -type StringPluralizationForms = 'one' | 'some' | 'many' | 'none'; - -declare function setImmediate(fn: AnyFunction): number; -declare function clearImmediate(id: number): void; - -declare function structuredClone(obj: T): T; - -interface Headers { - keys(): IterableIterator; - values(): IterableIterator; - entries(): IterableIterator<[string, string]>; - [Symbol.iterator]: IterableIterator<[string, string]>; -} - -type Primitive = - string | - symbol | - number | - bigint | - boolean | - undefined | - null; - -type JSONLikeValue = - string | - number | - boolean | - null | - JSONLikeValue[] | - Dictionary; - -type CanPromise = T | Promise; -type CanPromiseLike = T | PromiseLike; -type CanArray = T | T[]; - -type CanUndef = T | undefined; -type Nullable = T | null | undefined; - -// eslint-disable-next-line @typescript-eslint/no-invalid-void-type -type CanVoid = T | void; - -type AnyToIgnore = any; -type AnyToBoolean = any; -interface AnyFunction extends Function { - (...args: ARGS): R; -} - -interface AnyOneArgFunction extends Function { - (arg: ARG): R; -} - -type ClassConstructor = new (...args: ARGS) => R; - -interface StrictDictionary { - [key: PropertyKey]: T; -} - -interface Dictionary { - [key: PropertyKey]: CanUndef; -} - -interface Maybe extends Promise { - readonly type: 'Maybe'; -} - -interface Either extends Promise { - readonly type: 'Either'; -} - -type NewPromise = K extends Maybe ? - Maybe : K extends Either ? - Either : Promise; - -type PromiseType = - T extends Maybe ? - NonNullable : T extends Promise ? V : T; - -/** - * Wraps the specified function to return a value as Promise - * - * @typeparam T - any function - * - * @example - * ```typescript - * type A = typeof () => null; - * - * // () => Promise - * type B = ReturnPromise; - * ``` - */ -type ReturnPromise> = (...args: Parameters) => Promise>; - -type DictionaryType = T extends Dictionary ? NonNullable : T; - -type AnyIterable = Iterable | AsyncIterable; - -type IterableType | AsyncIterable> = - T extends Iterable ? - V : - T extends AsyncIterable ? V : T; - -/** - * Creates an interface based on the specified type or interface but every property can be edited - */ -type Writable = { - -readonly [K in keyof T]: T[K]; -}; - -/** - * Overrides properties of the specified type or interface. - * Don't use this helper if you simply extend one type from another, i.e. without overriding properties. - * - * @template T - original type - * @template U - type with the overridden properties - * - * @example - * ```typescript - * type A = { - * x: number; - * y: number; - * }; - * - * // {x:number; y: string} - * type B = Overwrite; - * ``` - */ -type Overwrite = Pick> & U; - -/** - * Returns a new non-abstract class from the specified abstract class where methods can have the default implementation. - * The default implementations are taken from the static methods that match by names with the class's methods. - * - * @example - * ```typescript - * abstract class iFoo { - * static bar(self: object): string { - * return self.bla.toString(); - * } - * - * bar(): string { - * return Object.throw(); - * } - * - * abstract bar(): number; - * } - * - * // class { bar(): string; } - * Trait - * ``` - */ -type Trait = { - [K in Extract]: I[K]; -}; - -/** - * Returns a new function based on the specified with adding as the first parameter the passed object - * - * @example - * ```typescript - * // (self: bFoo, a: number) => string - * AddSelf<(a: number) => string, bFoo> - * ``` - */ -type AddSelf = M extends (...args: infer A) => infer R ? - (self: S, ...args: A) => R : - never; - -interface JSONCb { - (key: string, value: unknown): unknown; -} - -interface FastCloneOptions { - /** - * Replacer function for `JSON.stringify` - * @see [[JSON.stringify]] - */ - replacer?: JSONCb; - - /** - * Reviver function for `JSON.parse` - * @see [[JSON.parse]] - */ - reviver?: JSONCb; - - /** - * If false the object freeze/seal state won't be copy - * @default `true` - */ - freezable?: boolean; -} - -interface ObjectMixinOptions { - /** - * If true, then object properties are copied recursively. - * Also, this mode enables copying properties from a prototype. - * - * @default `false` - * @example - * ```js - * // {a: {c: 2}} - * Object.mixin({deep: false}, {a: {b: 1}}, {a: {c: 2}}); - * - * // {a: {b: 1, c: 2}} - * Object.mixin({deep: true}, {a: {b: 1}}, {a: {c: 2}}); - * - * // {a: {c: 2}} - * Object.mixin({deep: true}, {}, {__proto__: {a: {c: 2}}}); - * ``` - */ - deep?: boolean; - - /** - * Strategy to resolve collisions of properties when merging: - * 1. `'all'` - all properties are merged in spite of possible collisions (by default) - * 2. `'new'` - properties with collisions aren't merged - * 3. `'exist'` - properties without collisions aren't merged - * - * @default `'all'` - * @example - * ```js - * // {a: 2, b: 3} - * Object.mixin({propsToCopy: 'all'}, {a: 1}, {a: 2, b: 3}); - * - * // {a: 1, b: 3} - * Object.mixin({propsToCopy: 'new'}, {a: 1}, {a: 2, b: 3}); - * - * // {a: 2} - * Object.mixin({propsToCopy: 'exist'}, {a: 1}, {a: 2, b: 3}); - * ``` - */ - propsToCopy?: 'new' | 'exist' | 'all'; - - /** - * Function to filter values that shouldn't be copied - * - * @param el - element value - * @param key - element key - * @param data - element container - * - * @example - * ```js - * // {a: 1, b: 2} - * Object.mixin({deep: true}, {a: 1}, {b: 2}); - * - * // {a: 1} - * Object.mixin({deep: true, filter: (el, key) => key !== 'b'}, {a: 1}, {b: 2}); - * ``` - */ - filter?(el: V, key: K, data: D): AnyToBoolean; - - /** - * Function to filter values that support deep extending - * (works only with the `deep` mode) - * - * @param el - element value - * @param key - element key - * @param data - element container - * - * @example - * ```js - * // {a: {a: 1, b: 2}} - * Object.mixin({deep: true}, {a: {a: 1}}, {a: {b: 2}}); - * - * // {a: {b: 2}} - * Object.mixin({deep: true, extendFilter: (el) => !el.b}, {a: {a: 1}}, {a: {b: 2}}); - * ``` - */ - extendFilter?(el: unknown, key: K, data: V): AnyToBoolean; - - /** - * If true, all properties with undefined value aren't copied - * - * @default `true` - * @example - * ```js - * // {a: 1} - * Object.mixin({skipUndefs: true}, {a: 1}, {a: undefined}); - * - * // {a: undefined} - * Object.mixin({skipUndefs: false}, {a: 1}, {a: undefined}); - * ``` - */ - skipUndefs?: boolean; - - /** - * If true, the function will merge all object properties, but not only enumerable. - * Non-enumerable properties from a prototype are ignored. - * - * @default `false` - * @example - * ```js - * const obj = {a: 1}; - * - * Object.defineProperty(obj, 'b', {value: 2}); - * - * // {a: 1, b: 2} - * Object.mixin({withNonEnumerables: true}, {}, obj); - * ``` - */ - withNonEnumerables?: boolean; - - /** - * Should or shouldn't copy property descriptors too. - * If passed `onlyAccessors`, the descriptor properties like `enumerable` or `configurable` are ignored. - * - * @default `false` - */ - withDescriptors?: boolean | 'onlyAccessors'; - - /** - * If true, then merging preserve prototypes of properties - * (works only with the `deep` mode) - * - * @default `false` - * @example - * ```js - * const proto = { - * a: { - * b: 2 - * }, - * - * c: 3 - * }; - * - * const obj = Object.create(proto); - * Object.mixin({deep: true, withProto: false}, obj, {c: 2, a: {d: 4}}); - * - * // 2 - * // 4 - * // 2 - * // true - * // true - * console.log( - * obj.c, - * obj.a.d, - * obj.a.b, - * obj.a.hasOwnProperty('d'), - * obj.a.hasOwnProperty('b') - * ); - * - * const obj2 = Object.create(proto); - * Object.mixin({deep: true, withProto: true}, obj2, {c: 2, a: {d: 4}}); - * - * // 2 - * // 4 - * // 2 - * // true - * // false - * console.log( - * obj2.c, - * obj2.a.d, - * obj2.a.b, - * obj2.a.hasOwnProperty('d'), - * obj2.a.hasOwnProperty('b') - * ); - * ``` - */ - withProto?: boolean; - - /** - * If true, then to merge two arrays will be used a concatenation strategy (works only with the `deep` mode). - * Also, the parameter can be passed as a function to concatenate arrays. - * - * @default `false` - * @example - * ```js - * // {a: [2]} - * Object.mixin({deep: true, concatArrays: false}, {a: [1]}, {a: [2]}); - * - * // {a: [1, 2]} - * Object.mixin({deep: true, concatArrays: true}, {a: [1]}, {a: [2]}); - * - * // {a: [1, 1, 2]} - * Object.mixin({deep: true, concatArrays: true}, {a: [1]}, {a: [1, 2]}); - * - * // {a: [1, 2]} - * Object.mixin({deep: true, concatArrays: (a, b) => a.union(b)}, {a: [1]}, {a: [1, 2]}); - * ``` - */ - concatArrays?: boolean | ((a: unknown[], b: unknown[], key: K) => unknown[]); - - /** - * @deprecated - * @see [[ObjectMixinOptions.concatArrays]] - */ - concatArray?: boolean; - - /** - * @deprecated - * @see [[ObjectMixinOptions.concatArrays]] - */ - concatFn?(a: unknown[], b: unknown[], key: K): unknown[]; - - /** - * @deprecated - * @see [[ObjectMixinOptions.propsToCopy]] - */ - onlyNew?: boolean | -1; - - /** - * @deprecated - * @see [[ObjectMixinOptions.propsToCopy]] - */ - traits?: boolean | -1; - - /** - * @deprecated - * @see [[ObjectMixinOptions.skipUndefs]] - */ - withUndef?: boolean; - - /** - * @deprecated - * @see [[ObjectMixinOptions.withDescriptors]] - */ - withDescriptor?: boolean; - - /** - * @deprecated - * @see [[ObjectMixinOptions.withDescriptors]] - */ - withAccessors?: boolean; -} - -interface ObjectGetOptions { - /** - * Character to declare the path - * - * @example - * ```js - * Object.get({a: {b: 1}}, 'a:b', {separator: ':'}) - * ``` - */ - separator?: string; -} - -interface ObjectSetOptions extends ObjectGetOptions { - /** - * If true, then a new value will be concatenated with the old - * - * @example - * ```js - * const obj = {a: {b: 1}}; - * Object.set(obj, 'a.b', 2, {concat: true}) - * console.log(obj); // [1, 2] - * ``` - */ - concat?: boolean; - - /** - * Function to set a value - * - * @param obj - * @param key - * @param value - */ - setter?(obj: unknown, key: unknown, value: unknown); -} - -interface ObjectForEachOptions { - /** - * If true, then the callback function takes an element descriptor instead of a value - * - * @default `false` - * @example - * ```js - * Object.forEach({a: 1}, {showDescriptor: true}, (el) => { - * // {configurable: true, enumerable: true, writable: true, value: 1} - * console.log(el); - * }); - * ``` - */ - passDescriptor?: boolean; - - /** - * Strategy to iterate object properties: - * 1. `'own'` - the object iterates only own properties (by default) - * 2. `'inherited'` - the object iterates only inherited properties - * (for-in with the negative `hasOwnProperty` check) - * - * 3. `'all'` - the object iterates inherited properties too (for-in without the `hasOwnProperty` check) - * - * @example - * ```js - * const obj = {a: 1, __proto__: {b: 2}}; - * - * Object.forEach(obj, (el) => { - * console.log(el); // 1 - * }); - * - * Object.forEach(obj, {propsToIterate: 'all'}, (el) => { - * console.log(el); // 1 2 - * }); - * - * Object.forEach(obj, {propsToIterate: 'inherited'}, (el) => { - * console.log(el); // 2 - * }); - * ``` - */ - propsToIterate?: 'own' | 'inherited' | 'all'; - - /** - * If true, the function will iterate all object properties, but not only enumerable. - * Non-enumerable properties from a prototype are ignored. - * - * @default `false` - * @example - * ```js - * const obj = {a: 1}; - * - * Object.defineProperty(obj, 'b', {value: 2}); - * - * // 1 - * // 2 - * Object.forEach(obj, {withNonEnumerables: true}, (el) => { - * console.log(el); - * }); - * ``` - */ - withNonEnumerables?: boolean; - - /** - * @deprecated - * @see [[ObjectForEachOptions.passDescriptor]] - */ - withDescriptor?: boolean; - - /** - * @deprecated - * @see [[ObjectForEachOptions.propsToIterate]] - */ - notOwn?: boolean | -1; -} - -interface ObjectForEachPropertyDescriptor { - configurable?: boolean; - enumerable?: boolean; - value?: V; - writable?: boolean; - get?(): unknown; - set?(v: unknown): void; -} - -interface ObjectFromArrayOptions { - /** - * Function that returns a key value - * - * @param el - element value - * @param i - element index - */ - key?(el: unknown, i: number): PropertyKey; - - /** - * @deprecated - * @see [[ObjectFromArrayOptions.key]] - */ - keyConverter?(i: number, el: unknown): PropertyKey; - - /** - * Function that returns an element value - * - * @param el - element value - * @param i - element index - */ - value?(el: unknown, i: number): T; - - /** - * @deprecated - * @see [[ObjectFromArrayOptions.value]] - */ - valueConverter?(el: unknown, i: number): T; -} - -type ObjectPropertyPath = - string | - any[]; - -interface ObjectPropertyFilter { - (key: K, el: V): AnyToBoolean; -} - -interface ObjectConstructor { - /** - * Returns a value from the passed object by the specified path. - * Returns undefined if the specified path doesn't exist in the object. - * The method can access properties through promises. - * - * @param obj - * @param path - * @param [opts] - additional options - */ - get(obj: any, path: ObjectPropertyPath, opts?: ObjectGetOptions): CanUndef; - - /** - * Returns a function that returns a value from the passed object, which the function takes, by the specified path. - * The function returns undefined if the specified path doesn't exist in the object. - * - * @param path - * @param [opts] - additional options - */ - get(path: ObjectPropertyPath, opts?: ObjectGetOptions): (obj: any) => CanUndef; - - /** - * Returns a function that returns a value from the specified object by a path that the function takes. - * The function returns undefined if the specified path doesn't exist in the object. - * - * @param obj - * @param [opts] - additional options - */ - get(obj: any, opts?: ObjectGetOptions): (path: ObjectPropertyPath) => CanUndef; - - /** - * Returns true if an object has a property by the specified path - * - * @param obj - * @param path - * @param [opts] - additional options - */ - has(obj: any, path: ObjectPropertyPath, opts?: ObjectGetOptions): boolean; - - /** - * Returns a function that returns true if an object, which the function takes, has a value by the specified path - * - * @param path - * @param [opts] - additional options - */ - has(path: ObjectPropertyPath, opts?: ObjectGetOptions): (obj: any) => boolean; - - /** - * Returns a function that returns true if the specified object has a value by a path that the function takes - * - * @param obj - * @param [opts] - additional options - */ - has(obj: any, opts?: ObjectGetOptions): (path: ObjectPropertyPath) => boolean; - - /** - * Returns a function that returns true if the passed object, which the function takes, - * has own property by the specified key - * - * @param key - */ - hasOwnProperty(key: PropertyKey): (obj: any) => boolean; - - /** - * Returns a function that returns true if the specified object has own property by a key that the function takes - * @param obj - */ - hasOwnProperty(obj: any): (key: PropertyKey) => boolean; - - /** - * Returns true if the passed object has an own property by the specified key - * - * @param obj - * @param key - */ - hasOwnProperty(obj: any, key: PropertyKey): boolean; - - /** - * Sets the passed symbol property to the given object. - * The character is set as non-enumerable to avoid implicit copying when using the spread operation. - * - * @param obj - * @param symbol - * @param value - * */ - defineSymbol(obj: T, symbol: symbol, value: any): T; - - /** - * Sets a value to the passed object by the specified path. - * The final function returns a value that was added. - * - * @param obj - * @param path - * @param value - * @param [opts] - additional options - */ - set(obj: any, path: ObjectPropertyPath, value: T, opts?: ObjectSetOptions): CanUndef; - - /** - * Returns a function that sets a value to an object, which the function takes, by the specified path. - * The final function returns a link to the object. - * - * @param path - * @param [opts] - additional options - * @param [value] - */ - set(path: ObjectPropertyPath, opts?: ObjectSetOptions, value?: any): (obj: T, value?: any) => CanUndef; - - /** - * Returns a function that sets a value to the specified object by a path that the function takes. - * The final function returns a link to the object. - * - * @param obj - * @param [opts] - additional options - * @param [value] - */ - set(obj: T, opts?: ObjectSetOptions, value?: any): (path: ObjectPropertyPath, value?: any) => CanUndef; - - /** - * Deletes a value from an object by the specified path - * - * @param obj - * @param path - * @param [opts] - additional options - */ - delete(obj: any, path: ObjectPropertyPath, opts?: ObjectGetOptions): boolean; - - /** - * Returns a function that deletes a value from an object, which the function takes, by the specified path - * - * @param path - * @param [opts] - additional options - */ - delete(path: ObjectPropertyPath, opts?: ObjectGetOptions): (obj: any) => boolean; - - /** - * Returns a function that deletes a value from the specified object by a path that the function takes - * - * @param obj - * @param [opts] - additional options - */ - delete(obj: any, opts?: ObjectGetOptions): (path: ObjectPropertyPath) => boolean; - - /** - * Returns size/length of the specified object - * - * @param obj - * - * @example - * ```js - * // 1 - * Object.size({a: 1}); - * - * // 2 - * Object.size([1, 2]); - * - * // 2 - * Object.size(new Set([1, 2])); - * - * // 2 - * Object.size((a, b) => a + b)); - * - * // 1 - * Object.size(1); - * - * // 0 - * Object.size(NaN); - * Object.size(null); - * Object.size(undefined); - * ``` - */ - size(obj: any): number; - - /** - * Iterates over the specified dictionary - * - * @param obj - object to iterate - * @param opts - additional options - * @param cb - callback function that is called on each of object elements - */ - forEach( - obj: Dictionary, - opts: ObjectForEachOptions & {withDescriptors: true}, - cb: (el: ObjectForEachPropertyDescriptor, key: string, data: Dictionary) => any - ): void; - - /** - * Iterates over the specified dictionary - * - * @param obj - object to iterate - * @param opts - additional options - * @param cb - callback function that is called on each of object elements - */ - forEach( - obj: Dictionary, - opts: ObjectForEachOptions & ({propsToIterate: 'all' | 'notOwn'} | {withDescriptors: false}), - cb: (el: V, key: string, data: Dictionary) => any - ): void; - - /** - * Iterates over the specified Map object - * - * @param obj - object to iterate - * @param opts - additional options - * @param cb - callback function that is called on each of object elements - */ - forEach( - obj: Map, - opts: ObjectForEachOptions, - cb: (el: V, key: K, data: Map) => any - ): void; - - /** - * Iterates over the specified Map object - * - * @param obj - object to iterate - * @param cb - callback function that is called on each of object elements - * @param [opts] - additional options - */ - forEach( - obj: Map, - cb: (el: V, key: K, data: Map) => any, - opts?: ObjectForEachOptions - ): void; - - /** - * Iterates over the specified Set object - * - * @param obj - object to iterate - * @param opts - additional options - * @param cb - callback function that is called on each of object elements - */ - forEach( - obj: Set, - opts: ObjectForEachOptions, - cb: (el: V, i: V, data: Set) => any - ): void; - - /** - * Iterates over the specified Set object - * - * @param obj - object to iterate - * @param cb - callback function that is called on each of object elements - * @param [opts] - additional options - */ - forEach( - obj: Set, - cb: (el: V, i: V, data: Set) => any, - opts?: ObjectForEachOptions - ): void; - - /** - * Iterates over the specified array - * - * @param obj - object to iterate - * @param opts - additional options - * @param cb - callback function that is called on each of object elements - */ - forEach( - obj: V[], - opts: ObjectForEachOptions, - cb: (el: V, i: number, data: V[]) => any - ): void; - - /** - * Iterates over the specified array - * - * @param obj - object to iterate - * @param cb - callback function that is called on each of object elements - * @param [opts] - additional options - */ - forEach( - obj: V[], - cb: (el: V, i: number, data: V[]) => any, - opts?: ObjectForEachOptions - ): void; - - /** - * Iterates over the specified array-like object - * - * @param obj - object to iterate - * @param opts - additional options - * @param cb - callback function that is called on each of object elements - */ - forEach( - obj: ArrayLike, - opts: ObjectForEachOptions, - cb: (el: V, i: number, data: ArrayLike) => any, - ): void; - - /** - * Iterates over the specified array-like object - * - * @param obj - object to iterate - * @param cb - callback function that is called on each of object elements - * @param [opts] - additional options - */ - forEach( - obj: ArrayLike, - cb: (el: V, i: number, data: ArrayLike) => any, - opts?: ObjectForEachOptions - ): void; - - /** - * Iterates over the specified iterable object - * - * @param obj - object to iterate - * @param opts - additional options - * @param cb - callback function that is called on each of object elements - */ - forEach( - obj: Iterable, - opts: ObjectForEachOptions, - cb: (el: V, key: null, data: Iterable) => any - ): void; - - /** - * Iterates over the specified iterable object - * - * @param obj - object to iterate - * @param cb - callback function that is called on each of object elements - * @param [opts] - additional options - */ - forEach( - obj: Iterable, - cb: (el: V, key: null, data: Iterable) => any, - opts?: ObjectForEachOptions - ): void; - - /** - * Iterates over the specified object - * - * @param obj - object to iterate - * @param opts - additional options - * @param cb - callback function that is called on each of object elements - */ - forEach( - obj: Dictionary, - opts: ObjectForEachOptions, - cb: (el: V, key: string, data: Dictionary) => any - ): void; - - /** - * Iterates over the specified object - * - * @param obj - object to iterate - * @param cb - callback function that is called on each of object elements - * @param [opts] - additional options - */ - forEach( - obj: Dictionary, - cb: (el: V, key: string, data: Dictionary) => any, - opts?: ObjectForEachOptions - ): void; - - /** - * Iterates over the specified object - * - * @param obj - object to iterate - * @param opts - additional options - * @param cb - callback function that is called on each of object elements - */ - forEach( - obj: D, - opts: ObjectForEachOptions, - cb: (el: V, key: K, data: D) => any - ): void; - - /** - * Iterates over the specified object - * - * @param obj - object to iterate - * @param cb - callback function that is called on each of object elements - * @param [opts] - additional options - */ - forEach( - obj: D, - cb: (el: V, key: K, data: D) => any, - opts?: ObjectForEachOptions - ): void; - - /** - * Returns a curried version of `Object.fastCompare` for one argument - * @param a - */ - fastCompare(a: any): (b: any) => boolean; - - /** - * Compares two specified objects by using a naive but fast `JSON.stringify/parse` strategy and - * returns true if they are equal. - * - * Mind, that this method uses non-stable version `JSON.stringify`, i.e., - * it can work incorrectly with object like `{a: 1, b: 2}` and `{b: 2, a: 1}`. - * - * Be careful with comparing `undefined` and `NaN` values, as they can be converted to `null` due to - * the nature of `JSON.stringify`. - * - * @param a - * @param b - */ - fastCompare(a: any, b: T): a is T; - - /** - * Returns a curried version of `Object.fastClone` - * - * @param obj - * @param opts - additional options - */ - fastClone(obj: undefined, opts: FastCloneOptions): (obj: T) => T; - - /** - * Clones the specified object using the `structuredClone` method if possible and returns a new object. - * Otherwise, the method will use a naive but fast `JSON.stringify/parse` strategy. - * - * Be careful with cloning `undefined` and `NaN` values, as they can be converted to `null` due to - * the nature of `JSON.stringify/parse`. - * - * @param obj - * @param [opts] - additional options - */ - fastClone(obj: T, opts?: FastCloneOptions): T; - - /** - * Returns a string representation of the specified object by using a naive but fast `JSON.stringify/parse` strategy. - * - * Mind, that this method uses non-stable version `JSON.stringify`, i.e., - * it can work incorrectly with object like `{a: 1, b: 2}` and `{b: 2, a: 1}`. - * - * Be careful with comparing `undefined` and `NaN` values, as they can be converted to `null` due to - * the nature of `JSON.stringify`. - * - * @param obj - */ - fastHash(obj: any): string; - - /** - * Returns a curried version of `Object.mixin` for one argument - * @param opts - if true, then properties will be copied recursively, or additional options to extend - */ - mixin(opts: ObjectMixinOptions | boolean): (base: B, obj1: O1) => B & O1; - - /** - * Returns a curried version of `Object.mixin` for one argument - * @param opts - if true, then properties will be copied recursively, or additional options to extend - */ - mixin(opts: ObjectMixinOptions | boolean): (...objects: any[]) => R; - - /** - * Returns a curried version of `Object.mixin` for two arguments - * - * @param opts - if true, then properties will be copied recursively, or additional options to extend - * @param target - target object - */ - mixin(opts: ObjectMixinOptions | boolean, target: B): (obj1: O1) => B & O1; - - /** - * Returns a curried version of `Object.mixin` for two arguments - * - * @param opts - if true, then properties will be copied recursively, or additional options to extend - * @param target - target object - */ - mixin(opts: ObjectMixinOptions | boolean, target: any): (...objects: any[]) => R; - - /** - * Extends the specified object by another objects. - * If the base value is not an object, a new object will be created with a type similar to the first extension object. - * - * @param opts - if true, then properties will be copied recursively, or additional options to extend - * @param target - target object - * @param obj1 - object for extending - */ - mixin(opts: ObjectMixinOptions | boolean, target: B, obj1: O1): B & O1; - - /** - * Extends the specified object by another objects. - * If the base value is not an object, a new object will be created with a type similar to the first extension object. - * - * @param opts - if true, then properties will be copied recursively, or additional options to extend - * @param target - target object - * @param obj1 - object for extending - * @param obj2 - object for extending - */ - mixin(opts: ObjectMixinOptions | boolean, target: B, obj1: O1, obj2: O2): B & O1 & O2; - - /** - * Extends the specified object by another objects. - * If the base value is not an object, a new object will be created with a type similar to the first extension object. - * - * @param opts - if true, then properties will be copied recursively, or additional options to extend - * @param target - target object - * @param obj1 - object for extending - * @param obj2 - object for extending - * @param obj3 - object for extending - */ - mixin(opts: ObjectMixinOptions | boolean, target: B, obj1: O1, obj2: O2, obj3: O3): B & O1 & O2 & O3; - - /** - * Extends the specified object by another objects. - * If the base value is not an object, a new object will be created with a type similar to the first extension object. - * - * @param opts - if true, then properties will be copied recursively, or additional options to extend - * @param target - target object - * @param objects - objects for extending - */ - mixin(opts: ObjectMixinOptions | boolean, target?: any, ...objects: any[]): R; - - /** - * Returns a curried version of `Object.serialize` - * @param replacer - replacer function to serialize - */ - trySerialize(replacer?: JSONCb): (value: V) => string | V; - - /** - * Tries to serialize the specified value into a string. - * - * If the value is an array, dictionary, or string or has the predefined `toJSON` method, it is serialized using - * `JSON.stringify`. In other cases, the value isn't serialized and will be returned by the function. - * Also, in the case of error during serialization, the function returns the original value. - * - * @param value - * @param [replacer] - replacer function to serialize - */ - trySerialize(value: V, replacer?: JSONCb): string | V; - - /** - * Returns a curried version of `Object.parse` - * @param reviver - reviver function to parse - */ - parse(reviver?: JSONCb): (value: V) => V extends string ? R : V; - - /** - * Parses the specified value as a JSON/JS object and returns the result of parsing. - * If the value isn't a string or can't be parsed, the function returns the original value. - * - * @param value - * @param [reviver] - reviver function to parse - */ - parse(value: V, reviver?: JSONCb): V extends string ? R : V; - - /** - * Creates a hash table without any prototype and returns it - * - * @example - * ```js - * Object.createDict() // {__proto__: null} - * ``` - */ - createDict(): Dictionary; - - /** - * Creates a hash table without any prototype and returns it - * - * @param objects - extension objects: - * all keys from these objects are merged to the target - * - * @example - * ```js - * Object.createDict({a: 1}, {b: 2}) // {a: 1, b: 2, __proto__: null} - * ``` - */ - createDict(...objects: Array>): Pick; - - /** - * Takes the enum-like object and converts it to a dictionary: - * number values from the object are skipped - * - * @param obj - */ - convertEnumToDict(obj: Nullable): {[K in keyof D]: K}; - - /** - * Creates an object which has the similar structure to the TS enum and returns it - * - * @param obj - base object: it can be a dictionary or an array - * @example - * ```js - * Object.createEnumLike({a: 1}) // {a: 1, 1: 'a', __proto__: null} - * ``` - */ - createEnumLike(obj: Nullable): - D extends Array ? Dictionary : D & {[I: string]: K}; - - /** - * @deprecated - * @see ObjectConstructor.createEnumLike - */ - createMap(obj: Nullable): - D extends Array ? Dictionary : D & {[I: string]: K}; - - /** - * Creates an object from the specified array - * - * @param arr - * @param [opts] - additional options - * - * @example - * ```js - * // {foo: true, bar: true} - * Object.fromArray(['foo', 'bar']); - * - * // {foo: 0, bar: 1} - * Object.fromArray(['foo', 'bar'], {value: (val, i) => i}); - * ``` - */ - fromArray(arr: Nullable, opts?: ObjectFromArrayOptions): Dictionary; - - /** - * Returns a curried version of `Object.select` - * @param condition - regular expression to filter by keys - */ - select(condition: RegExp | ObjectPropertyFilter): (obj: Nullable) => {[K in keyof D]?: D[K]}; - - /** - * Returns a curried version of `Object.select` - * @param condition - whitelist of keys to filter - */ - select(condition: []): (obj: D) => D; - - /** - * Returns a curried version of `Object.select` - * @param condition - whitelist of keys to filter - */ - select(condition: C | [C]): (obj: Nullable) => Pick>; - - /** - * Returns a curried version of `Object.select` - * @param condition - whitelist of keys to filter - */ - select< - C1 extends string, - C2 extends string - >(condition: [C1, C2]): (obj: Nullable) => Pick>; - - /** - * Returns a curried version of `Object.select` - * @param condition - whitelist of keys to filter - */ - select< - C1 extends string, - C2 extends string, - C3 extends string, - >(condition: [C1, C2, C3]): (obj: Nullable) => Pick>; - - /** - * Returns a curried version of `Object.select` - * @param condition - whitelist of keys to filter - */ - select< - C1 extends string, - C2 extends string, - C3 extends string, - C4 extends string - >(condition: [C1, C2, C3, C4]): (obj: Nullable) => Pick>; - - /** - * Returns a curried version of `Object.select` - * @param condition - whitelist of keys to filter - */ - select< - C1 extends string, - C2 extends string, - C3 extends string, - C4 extends string, - C5 extends string - >(condition: [C1, C2, C3, C4, C5]): (obj: Nullable) => - Pick>; - - /** - * Returns a curried version of `Object.select` - * @param condition - whitelist of keys to filter - */ - select(condition: Iterable): (obj: Nullable) => {[K in keyof D]?: D[K]}; - - /** - * Returns a curried version of `Object.select` - * @param condition - map of keys to filter - */ - select(condition: C): (obj: Nullable) => Pick>; - - /** - * Returns a new object based on the specified, but only with fields that match to the specified condition - * - * @param obj - * @param condition - regular expression to filter by keys - */ - select(obj: Nullable, condition: RegExp | ObjectPropertyFilter): {[K in keyof D]?: D[K]}; - - /** - * Returns a new object based on the specified, but only with fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - select(obj: Nullable, condition: []): D; - - /** - * Returns a new object based on the specified, but only with fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - select(obj: Nullable, condition: C | [C]): Pick>; - - /** - * Returns a new object based on the specified, but only with fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - select< - D extends object, - C1 extends string, - C2 extends string - >(obj: Nullable, condition: [C1, C2]): Pick>; - - /** - * Returns a new object based on the specified, but only with fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - select< - D extends object, - C1 extends string, - C2 extends string, - C3 extends string - >(obj: Nullable, condition: [C1, C2, C3]): Pick>; - - /** - * Returns a new object based on the specified, but only with fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - select< - D extends object, - C1 extends string, - C2 extends string, - C3 extends string, - C4 extends string - >(obj: Nullable, condition: [C1, C2, C3, C4]): Pick>; - - /** - * Returns a new object based on the specified, but only with fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - select< - D extends object, - C1 extends string, - C2 extends string, - C3 extends string, - C4 extends string, - C5 extends string, - >(obj: Nullable, condition: [C1, C2, C3, C4, C5]): Pick>; - - /** - * Returns a new object based on the specified, but only with fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - select(obj: Nullable, condition: Iterable): {[K in keyof D]?: D[K]}; - - /** - * Returns a new object based on the specified, but only with fields that match to the specified condition - * - * @param obj - * @param condition - map of keys to filter - */ - select(obj: Nullable, condition: C): Pick>; - - /** - * Returns a curried version of `Object.reject` - * @param condition - regular expression to filter by keys - */ - reject(condition: RegExp | ObjectPropertyFilter): (obj: Nullable) => {[K in keyof D]?: D[K]}; - - /** - * Returns a curried version of `Object.reject` - * @param condition - whitelist of keys to filter - */ - reject(condition: []): (obj: Nullable) => D; - - /** - * Returns a curried version of `Object.reject` - * @param condition - whitelist of keys to filter - */ - reject(condition: C | [C]): (obj: Nullable) => Omit; - - /** - * Returns a curried version of `Object.reject` - * @param condition - whitelist of keys to filter - */ - reject< - C1 extends string, - C2 extends string - >(condition: [C1, C2]): (obj: Nullable) => Omit; - - /** - * Returns a curried version of `Object.reject` - * @param condition - whitelist of keys to filter - */ - reject< - C1 extends string, - C2 extends string, - C3 extends string - >(condition: [C1, C2, C3]): (obj: Nullable) => Omit; - - /** - * Returns a curried version of `Object.reject` - * @param condition - whitelist of keys to filter - */ - reject< - C1 extends string, - C2 extends string, - C3 extends string, - C4 extends string - >(condition: [C1, C2, C3, C4]): (obj: Nullable) => Omit; - - /** - * Returns a curried version of `Object.reject` - * @param condition - whitelist of keys to filter - */ - reject< - C1 extends string, - C2 extends string, - C3 extends string, - C4 extends string, - C5 extends string - >(condition: [C1, C2, C3, C4, C5]): (obj: Nullable) => Omit; - - /** - * Returns a curried version of `Object.reject` - * @param condition - whitelist of keys to filter - */ - reject(condition: Iterable): (obj: Nullable) => {[K in keyof D]?: D[K]}; - - /** - * Returns a curried version of `Object.reject` - * @param condition - map of keys to filter - */ - reject(condition: C): (obj: Nullable) => Omit; - - /** - * Returns a new object based on the specified, but without fields that match to the specified condition - * - * @param obj - * @param condition - regular expression to filter by keys - */ - reject(obj: Nullable, condition: RegExp | ObjectPropertyFilter): {[K in keyof D]?: D[K]}; - - /** - * Returns a new object based on the specified, but without fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - reject(obj: Nullable, condition: []): D; - - /** - * Returns a new object based on the specified, but without fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - reject(obj: Nullable, condition: C | [C]): Omit; - - /** - * Returns a new object based on the specified, but without fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - reject< - D extends object, - C1 extends string, - C2 extends string - >(obj: Nullable, condition: [C1, C2]): Omit; - - /** - * Returns a new object based on the specified, but without fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - reject< - D extends object, - C1 extends string, - C2 extends string, - C3 extends string - >(obj: Nullable, condition: [C1, C2, C3]): Omit; - - /** - * Returns a new object based on the specified, but without fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - reject< - D extends object, - C1 extends string, - C2 extends string, - C3 extends string, - C4 extends string - >(obj: Nullable, condition: [C1, C2, C3, C4]): Omit; - - /** - * Returns a new object based on the specified, but without fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - reject< - D extends object, - C1 extends string, - C2 extends string, - C3 extends string, - C4 extends string, - C5 extends string - >(obj: Nullable, condition: [C1, C2, C3, C4, C5]): Omit; - - /** - * Returns a new object based on the specified, but without fields that match to the specified condition - * - * @param obj - * @param condition - whitelist of keys to filter - */ - reject(obj: Nullable, condition: Iterable): {[K in keyof D]?: D[K]}; - - /** - * Returns a new object based on the specified, but without fields that match to the specified condition - * - * @param obj - * @param condition - map of keys to filter - */ - reject(obj: Nullable, condition: C): Omit; - - /** - * Wraps the specified value into the Either structure. - * If the value is equal to null or undefined, the value is rejected. - * - * @example - * ```typescript - * function toLowerCase(str: string): string { - * return str.toLowerCase(); - * } - * - * Object.Option(toLowerCase)(null).catch((err) => err === null); - * Object.Option(null).catch((err) => err === null); - * - * Object.Option(toLowerCase)('FOO').then((value) => value === 'foo'); - * Object.Option('foo').then((value) => value === 'foo'); - * ``` - */ - Option(value: () => R): AnyFunction>; - - /** - * Wraps the specified value into the Either structure. - * If the value is equal to null or undefined, the value is rejected. - * - * @example - * ```typescript - * function toLowerCase(str: string): string { - * return str.toLowerCase(); - * } - * - * Object.Option(toLowerCase)(null).catch((err) => err === null); - * Object.Option(null).catch((err) => err === null); - * - * Object.Option(toLowerCase)('FOO').then((value) => value === 'foo'); - * Object.Option('foo').then((value) => value === 'foo'); - * ``` - */ - Option(value: (a1: A1, ...rest: A) => R): - (a1: Maybe> | Either | Nullable, ...rest: A) => Maybe; - - /** - * Wraps the specified value into the Either structure. - * If the value is equal to null or undefined, the value is rejected. - * - * @example - * ```typescript - * function toLowerCase(str: string): string { - * return str.toLowerCase(); - * } - * - * Object.Option(toLowerCase)(null).catch((err) => err === null); - * Object.Option(null).catch((err) => err === null); - * - * Object.Option(toLowerCase)('FOO').then((value) => value === 'foo'); - * Object.Option('foo').then((value) => value === 'foo'); - * ``` - */ - Option(value: T): Maybe; - - /** - * Wraps the specified value into the Either structure - * - * @example - * ```typescript - * function toLowerCase(str: string): string { - * return str.toLowerCase(); - * } - * - * Object.Result(toLowerCase)(null).catch((err) => err.message === 'str is null'); - * Object.Result(Object.result(toLowerCase)(null)).catch((err) => err.message === 'str is null'); - * - * Object.Result(toLowerCase)('FOO').then((value) => value === 'foo'); - * Object.Result('foo').then((value) => value === 'foo'); - * ``` - */ - Result(value: () => R): AnyFunction>; - - /** - * Wraps the specified value into the Either structure - * - * @example - * ```typescript - * function toLowerCase(str: string): string { - * return str.toLowerCase(); - * } - * - * Object.Result(toLowerCase)(null).catch((err) => err.message === 'str is null'); - * Object.Result(Object.result(toLowerCase)(null)).catch((err) => err.message === 'str is null'); - * - * Object.Result(toLowerCase)('FOO').then((value) => value === 'foo'); - * Object.Result('foo').then((value) => value === 'foo'); - * ``` - */ - Result(value: (a1: A1, ...a: A) => R): - (a1: Maybe | Either, ...rest: A) => Either; - - /** - * Wraps the specified value into the Either structure - * - * @example - * ```typescript - * function toLowerCase(str: string): string { - * return str.toLowerCase(); - * } - * - * Object.Result(toLowerCase)(null).catch((err) => err.message === 'str is null'); - * Object.Result(Object.result(toLowerCase)(null)).catch((err) => err.message === 'str is null'); - * - * Object.Result(toLowerCase)('FOO').then((value) => value === 'foo'); - * Object.Result('foo').then((value) => value === 'foo'); - * ``` - */ - Result(value: T): Either; - - /** - * Casts any value to the specified type - * @param value - */ - cast(value: any): T; - - /** - * Throws an error. - * This method is useful because the `throw` operator can't be used within expressions. - * - * @param [err] - */ - throw(err?: string | Error): any; - - /** - * Returns true if the specified value can be interpreted as true - * @param value - */ - isTruly(value: any): boolean; - - /** - * Returns true if the specified value has a primitive type - * @param value - */ - isPrimitive(value: any): value is Primitive; - - /** - * Returns true if the specified value is undefined - * @param value - */ - isUndef(value: any): value is undefined; - - /** - * Returns true if the specified value is null - * @param value - */ - isNull(value: any): value is null; - - /** - * Returns true if the specified value is null or undefined - * @param value - */ - isNullable(value: any): value is null | undefined; - - /** - * Returns true if the specified value is a string - * @param value - */ - isString(value: any): value is string; - - /** - * Returns true if the specified value is a number - * @param value - */ - isNumber(value: any): value is number; - - /** - * Returns true if the specified value is a boolean - * @param value - */ - isBoolean(value: any): value is boolean; - - /** - * Returns true if the specified value is a symbol - * @param value - */ - isSymbol(value: any): value is symbol; - - /** - * Returns true if the specified value is a regular expression - * @param value - */ - isRegExp(value: any): value is RegExp; - - /** - * Returns true if the specified value is a date - * @param value - */ - isDate(value: any): value is Date; - - /** - * Returns true if the specified value is an array - * @param value - */ - isArray(value: any): value is unknown[]; - - /** - * Returns true if the specified value is looks like an array - * @param value - */ - isArrayLike(value: any): value is ArrayLike; - - /** - * Returns true if the specified value is a map - * @param value - */ - isMap(value: any): value is Map; - - /** - * Returns true if the specified value is a weak map - * @param value - */ - isWeakMap(value: any): value is WeakMap; - - /** - * Returns true if the specified value is a set - * @param value - */ - isSet(value: any): value is Set; - - /** - * Returns true if the specified value is a weak set - * @param value - */ - isWeakSet(value: any): value is WeakSet; - - /** - * Returns true if the specified value is a dictionary. - * This method is similar to "isPlainObject", but it has another output TS type: - * instead of inferring of an output type the method always cast the type to a dictionary. - * - * @param value - * - * @example - * ```typescript - * interface Foo { - * bar(): number; - * } - * - * function foo(val: number | Foo) { - * if (Object.isPlainObject(val)) { - * val.bar(); // All fine - * } - * - * if (Object.isDictionary(val)) { - * val.bar(); // Warning: object is of type unknown - * } - * } - * ``` - */ - isDictionary(value: any): value is Dictionary; - - /** - * Returns true if the specified value is a plain object, - * i.e. it has `null` prototype or was constructed via `Object` - * - * @param obj - */ - isPlainObject(obj: T): obj is - // @ts-ignore (unsafe type cast) - T extends - any[] | - - Int8Array | - Int16Array | - Int32Array | - - Uint8Array | - Uint8ClampedArray | - Uint16Array | - Uint32Array | - - Float32Array | - Float64Array | - - ArrayBuffer | - SharedArrayBuffer | - DataView | - FormData | - Blob | - - Date | - RegExp | - Map | - WeakMap | - Set | - WeakSet | - Promise | - - Generator | - AnyFunction | - - /* eslint-disable @typescript-eslint/ban-types */ - - Number | - String | - Symbol | - Boolean | - - /* eslint-enable @typescript-eslint/ban-types */ - - Node | - Document | - Window | - Navigator | - Error | - - Intl.Collator | - Intl.DateTimeFormat | - Intl.NumberFormat - - ? Dictionary : T extends object ? NonNullable : Dictionary; - - /** - * Returns true if the specified value is a custom (not native) object or function - * @param value - */ - isCustomObject(value: any): boolean; - - /** - * Returns true if the specified value is a simple object (without a string type tag) - * @param value - */ - isSimpleObject(value: any): value is T; - - /** - * Returns true if the specified value is a function - * @param value - */ - isFunction(value: any): value is AnyFunction; - - /** - * Returns true if the specified value is a simple function. - * This method is similar to "isFunction", but it has another output TS type. - * - * @param value - */ - isSimpleFunction(value: any): value is Function; - - /** - * Returns true if the specified value is a generator function - * @param value - */ - isGenerator(value: any): value is GeneratorFunction; - - /** - * Returns true if the specified value is an async generator function - * @param value - */ - isAsyncGenerator(value: any): value is AsyncGeneratorFunction; - - /** - * Returns true if the specified value is an iterable structure - * @param value - */ - isIterable(value: any): value is Iterable; - - /** - * Returns true if the specified value is an async iterable structure - * @param value - */ - isAsyncIterable(value: any): value is AsyncIterable; - - /** - * Returns true if the specified value is an iterator - * @param value - */ - isIterator(value: any): value is Iterator; - - /** - * Returns true if the specified value is an async iterator - * @param value - */ - isAsyncIterator(value: any): value is AsyncIterator; - - /** - * Returns true if the specified value is a promise - * @param value - */ - isPromise(value: any): value is Promise; - - /** - * Returns true if the specified value is looks like a promise - * @param value - */ - isPromiseLike(value: any): value is PromiseLike; - - /** - * Returns true if the specified value is looks like a proxy - * @param value - */ - isProxy(value: T): T extends Primitive ? false : boolean; - - /** - * If the passed value is a proxy object, the method returns an original object. - * Otherwise, the passed object itself. - * - * @param value - */ - unwrapProxy(value: T): T; - - /** - * @deprecated - * @see [[ObjectConstructor.isPlainObject]] - * @see [[ObjectConstructor.isDictionary]] - */ - isObject(value: any): value is Dictionary; -} - -interface ArrayConstructor { - /** - * Returns a curried version of `Array.union` - * @param arr - */ - union>(arr: T): | any>( - ...args: Array | A> - ) => A extends Iterable ? - Array> | V> : - Array> | NonNullable>; - - /** - * Returns a new array containing elements from all specified non-primitive iterable values with duplicates removed. - * You can also pass non-iterable values, and they will be added to the final array, - * except values with `null` or `undefined`. - * - * @param arr - * @param args - */ - union, A extends Iterable | any>( - arr: T, - ...args: Array | A> - ): A extends Iterable ? - Array> | V> : - Array> | NonNullable>; - - /** - * Returns a curried version of `Array.concat` - * @param arr - */ - concat>(arr: T): >(...args: Array>) => - A extends Array ? - Array> | V> : - Array> | NonNullable>; - - /** - * Returns a new array containing elements from all specified arrays. - * You can also pass non-iterable values, and they will be added to the final array, - * except values with `null` or `undefined`. - * - * @param arr - * @param args - */ - concat, A extends CanArray>( - arr: T, - ...args: Array> - ): A extends Array ? - Array> | V> : - Array> | NonNullable>; -} - -interface Array { - /** - * Returns a new array containing elements from all specified non-primitive iterable values with duplicates removed. - * You can also pass non-iterable values, and they will be added to the final array, - * except values with `null` or `undefined`. - * - * @param args - */ - union | any>( - ...args: Array | A> - ): A extends Iterable ? Array : Array>; -} - -interface StringCapitalizeOptions { - /** - * If true, then remainder of the string will be transformed to lower case - * @default `false` - */ - lower?: boolean; - - /** - * If true, all words in the string will be capitalized - * @default `false` - */ - all?: boolean; - - /** - * If false, then the operation isn't cached - * @default `true` - */ - cache?: boolean; -} - -interface StringCamelizeOptions { - /** - * If false, then the first character of the string will be transformed to the lower case - * @default `true` - */ - upper?: boolean; - - /** - * If false, then the result string won't be cached - * @default `true` - */ - cache?: boolean; -} - -interface StringDasherizeOptions { - /** - * If true, then the operation can be reverted - * @default `false` - */ - stable?: boolean; - - /** - * If false, then the operation isn't cached - * @default `true` - */ - cache?: boolean; -} - -interface StringUnderscoreOptions extends StringDasherizeOptions { - -} - -interface StringConstructor { - /** - * Returns an iterator over the string letters. - * The method understands the composition of multiple Unicode symbols that produce one visual symbol. - * - * @example - * ``` - * [...String.letters('12🇷🇺👩')] // ['1', '2', '🇷🇺', '👩'] - * ``` - */ - letters(str: string): IterableIterator; - - /** - * Returns a curried version of `String.capitalize` - * @param opts - additional options - */ - capitalize(opts: StringCapitalizeOptions): (str: string) => string; - - /** - * Capitalizes the first character of the string and returns it - * - * @param str - * @param [opts] - additional options - */ - capitalize(str: string, opts?: StringCapitalizeOptions): string; - - /** - * Returns a curried version of `String.camelize` - * @param upper - if false, then the first character of a value is transformed to the lower case - */ - camelize(upper: boolean): (str: string) => string; - - /** - * Returns a curried version of `String.camelize` - * @param opts - additional options - */ - camelize(opts: StringCamelizeOptions): (str: string) => string; - - /** - * Returns a CamelCaseStyle version of the specified string - * - * @param str - * @param [upper] - if false, then the first character of a value is transformed to the lower case - */ - camelize(str: string, upper?: boolean): string; - - /** - * Returns a CamelCaseStyle version of the specified string - * - * @param str - * @param [opts] - additional options - */ - camelize(str: string, opts?: StringCamelizeOptions): string; - - /** - * Returns a curried version of `String.dasherize` - * @param stable - if true, then the operation can be reverted - */ - dasherize(stable: boolean): (str: string) => string; - - /** - * Returns a curried version of `String.dasherize` - * @param opts - additional options - */ - dasherize(opts: StringDasherizeOptions): (str: string) => string; - - /** - * Returns a dash-style version of the specified string - * - * @param str - * @param [stable] - if true, then the operation can be reverted - */ - dasherize(str: string, stable?: boolean): string; - - /** - * Returns a dash-style version of the specified string - * - * @param str - * @param [opts] - additional options - */ - dasherize(str: string, opts?: StringDasherizeOptions): string; - - /** - * Returns a curried version of `String.underscore` - * @param stable - if true, then the operation can be reverted - */ - underscore(stable: boolean): (str: string) => string; - - /** - * Returns a curried version of `String.underscore` - * @param opts - additional options - */ - underscore(opts: StringUnderscoreOptions): (str: string) => string; - - /** - * Returns an underscore_style version of the specified string - * - * @param str - * @param [stable] - if true, then the operation can be reverted - */ - underscore(str: string, stable?: boolean): string; - - /** - * Returns an underscore_style version of the specified string - * - * @param str - * @param [opts] - additional options - */ - underscore(str: string, opts?: StringUnderscoreOptions): string; -} - -interface String { - /** - * Returns an iterator over the string letters. - * The method understands the composition of multiple Unicode symbols that produce one visual symbol. - * - * @example - * ``` - * [...'12🇷🇺👩'.letters()] // ['1', '2', '🇷🇺', '👩'] - * ``` - */ - letters(): IterableIterator; - - /** - * Capitalizes the first character of a string and returns it - * @param [opts] - additional options - */ - capitalize(opts?: StringCapitalizeOptions): string; - - /** - * Returns a CamelCaseStyle version of the specified string - * @param [upper] - if false, then the first character of the string is transformed to the lower case - */ - camelize(upper?: boolean): string; - - /** - * Returns a CamelCaseStyle version of the specified string - * @param [opts] - additional options - */ - camelize(opts?: StringCamelizeOptions): string; - - /** - * Returns a dash-style version of the specified string - * @param [stable] - if true, then the operation can be reverted - */ - dasherize(stable?: boolean): string; - - /** - * Returns a dash-style version of the specified string - * @param [opts] - additional options - */ - dasherize(opts?: StringDasherizeOptions): string; - - /** - * Returns an underscore_style version of the specified string - * @param [stable] - if true, then the operation can be reverted - */ - underscore(stable?: boolean): string; - - /** - * Returns an underscore_style version of the specified string - * @param [opts] - additional options - */ - underscore(opts?: StringUnderscoreOptions): string; -} - -type NumberOption = - 'decimal' | - 'thousands'; - -interface NumberConstructor { - /** - * Returns an option value by the specified key - * - * @deprecated - * @param key - */ - getOption(key: NumberOption): string; - - /** - * Sets a new option value by the specified key - * - * @deprecated - * @param key - * @param value - */ - setOption(key: NumberOption, value: string): string; - - /** - * Returns true if the specified value is a safe number - * @param value - */ - isSafe(value: any): boolean; - - /** - * Returns true if the specified value is an integer number - * @param value - */ - isInteger(value: any): boolean; - - /** - * Returns true if the specified value is a float number - * @param value - */ - isFloat(value: any): boolean; - - /** - * Returns true if the specified value is an even number - * @param value - */ - isEven(value: any): boolean; - - /** - * Returns true if the specified value is an odd number - * @param value - */ - isOdd(value: any): boolean; - - /** - * Returns true if the specified value is a natural number - * @param value - */ - isNatural(value: any): boolean; - - /** - * Returns true if the specified value is a positive number - * @param value - */ - isPositive(value: any): boolean; - - /** - * Returns true if the specified value is a negative number - * @param value - */ - isNegative(value: any): boolean; - - /** - * Returns true if the specified value is a non-negative number - * @param value - */ - isNonNegative(value: any): boolean; - - /** - * Returns true if the specified value is a number and is more or equal than 0 and less or equal than 1 - * @param value - */ - isBetweenZeroAndOne(value: any): boolean; - - /** - * Returns true if the specified value is a number and is more than 0 and less or equal than 1 - * @param value - */ - isPositiveBetweenZeroAndOne(value: any): boolean; - - /** - * Returns a value of milliseconds from the seconds - */ - seconds(value: number): number; - - /** - * Returns a value of milliseconds from the minutes - */ - minutes(value: number): number; - - /** - * Returns a value of milliseconds from the hours - */ - hours(value: number): number; - - /** - * Returns a value of milliseconds from the days - */ - days(value: number): number; - - /** - * Returns a value of milliseconds from the weeks - */ - weeks(value: number): number; - - /** - * Returns a curried version of `Number.floor` - * @param precision - */ - floor(precision: number): (value: number) => number; - - /** - * Shortcut for Math.floor that also allows a precision - * - * @param value - * @param precision - */ - floor(value: number, precision: number): number; - - /** - * Returns a curried version of `Number.round` - * @param precision - */ - round(precision: number): (value: number) => number; - - /** - * Shortcut for Math.round that also allows a precision - * - * @param value - * @param precision - */ - round(value: number, precision: number): number; - - /** - * Returns a curried version of `Number.ceil` - * @param precision - */ - ceil(precision: number): (value: number) => number; - - /** - * Shortcut for Math.ceil that also allows a precision - * - * @param value - * @param precision - */ - ceil(value: number, precision: number): number; - - /** - * Returns a curried version of `Number.pad` - * @param opts - additional options - */ - pad(opts: NumberPadOptions): (value: string) => string; - - /** - * Returns a string from a number with adding extra zeros to the start, if necessary - * - * @param num - * @param targetLength - length of the resulting string once the current string has been padded - */ - pad(num: number, targetLength?: number): string; - - /** - * Returns a string from a number with adding extra zeros to the start, if necessary - * - * @param num - * @param opts - additional options - */ - pad(num: number, opts: NumberPadOptions): string; - - /** - * Returns a curried version of `Number.format` - * - * @param pattern - * @param locale - */ - format(pattern: string, locale?: CanArray): (value: number) => string; - - /** - * Returns a curried version of `Number.format` - * - * @param opts - * @param locale - */ - format(opts: Intl.NumberFormatOptions, locale?: CanArray): (value: number) => string; - - /** - * Returns a string representation of a number by the specified pattern. - * All pattern directives are based on the native `Intl.NumberFormat` options: - * - * 1. `'style'` - * 2. `'currency'` - * 3. `'currencyDisplay'` - * - * There are aliases for all directives: - * - * 1. `'$'` - `{style: 'currency', currency: 'USD'}` - * 2. `'$:${currency}'` - `{style: 'currency', currency}` - * 3. `'$d:${currencyDisplay}'` - `{currencyDisplay}` - * 4. `'%'` - `{style: 'percent'}` - * 5. `'.'` - `{style: 'decimal'}` - * - * @param num - * @param pattern - format string pattern: - * 1. symbol `';'` is used as a separator character for the pattern directives, for example: `'$;$d:code'` - * 2. symbol `':'` is used for specifying a custom value for a pattern directive, for example: - * `'$:RUB;$d:code'` - * - * @param [locale] - locale for internalizing - * - * @example - * ```js - * 100.50.format('$', 'en-us') // '$100.50' - * 100.50.format('$:EUR;$d:code', 'en-us') // 'EUR 100.50' - * ``` - */ - format(num: number, pattern?: string, locale?: CanArray): string; - - /** - * Returns a string representation of a number by the specified options - * - * @param num - * @param opts - formatting options - * @param [locale] - locale for internalizing - */ - format(num: number, opts: Intl.NumberFormatOptions, locale?: CanArray): string; -} - -interface NumberPadOptions { - /** - * Length of the resulting string once the current string has been padded - */ - length?: number; - - /** - * Value of the base to convert in a string - * @default `10` - */ - base?: number; - - /** - * If true, then a sign of the number will be written anyway - * @default `false` - */ - sign?: boolean; -} - -interface Number { - /** - * Returns a string: the value + 'em' - */ - em: string; - - /** - * Returns a string: the value + 'rem' - */ - rem: string; - - /** - * Returns a string: the value + 'px' - */ - px: string; - - /** - * Returns a string: the value + 'per' - */ - per: string; - - /** - * Returns a string: the value + 'vh' - */ - vh: string; - - /** - * Returns a string: the value + 'vw' - */ - vw: string; - - /** - * Returns a string: the value + 'vmin' - */ - vmin: string; - - /** - * Returns a string: the value + 'vmax' - */ - vmax: string; - - /** - * Returns a value of milliseconds from the seconds - */ - second(): number; - - /** - * Returns a value of milliseconds from the seconds - */ - seconds(): number; - - /** - * Returns a value of milliseconds from the minutes - */ - minute(): number; - - /** - * Returns a value of milliseconds from the minutes - */ - minutes(): number; - - /** - * Returns a value of milliseconds from the hours - */ - hour(): number; - - /** - * Returns a value of milliseconds from the hours - */ - hours(): number; - - /** - * Returns a value of milliseconds from the days - */ - day(): number; - - /** - * Returns a value of milliseconds from the days - */ - days(): number; - - /** - * Returns a value of milliseconds from the weeks - */ - week(): number; - - /** - * Returns a value of milliseconds from the weeks - */ - weeks(): number; - - /** - * Returns true if the number is safe - */ - isSafe(): boolean; - - /** - * Returns true if the number is an integer - */ - isInteger(): boolean; - - /** - * Returns true if the number is a float - */ - isFloat(): boolean; - - /** - * Returns true if the number is even - */ - isEven(): boolean; - - /** - * Returns true if the number is odd - */ - isOdd(): boolean; - - /** - * Returns true if the number is natural - */ - isNatural(): boolean; - - /** - * Returns true if the number is positive - */ - isPositive(): boolean; - - /** - * Returns true if the number is negative - */ - isNegative(): boolean; - - /** - * Returns true if the number is non-negative - */ - isNonNegative(): boolean; - - /** - * Returns true if the number is more or equal than 0 and less or equal than 1 - */ - isBetweenZeroAndOne(): boolean; - - /** - * Returns true if the number is more than 0 and less or equal than 1 - */ - isPositiveBetweenZeroAndOne(): boolean; - - /** - * Returns a string from the number with adding extra zeros to the start, if necessary - * - * @param targetLength - length of the resulting string once the current string has been padded - * @param [opts] - additional options - */ - pad(targetLength?: number, opts?: NumberPadOptions): string; - - /** - * Returns a string representation of the number by the specified pattern. - * All pattern directives are based on the native `Intl.NumberFormat` options: - * - * 1. `'style'` - * 1. `'currency'` - * 1. `'currencyDisplay'` - * - * There are aliases for all directives: - * - * 1. `'$'` - `{style: 'currency', currency: 'USD'}` - * 1. `'$:${currency}'` - `{style: 'currency', currency}` - * 1. `'$d:${currencyDisplay}'` - `{currencyDisplay}` - * 1. `'%'` - `{style: 'percent'}` - * 1. `'.'` - `{style: 'decimal'}` - * - * @param pattern - string pattern of the format: - * 1. symbol `';'` is used as a separator character for pattern directives, for example: `'$;$d:code'` - * 1. symbol `':'` is used for specifying a custom value for a pattern directive, for example: - * `'$:RUB;$d:code'` - * - * @param [locale] - locale for internalizing - * - * @example - * ```js - * 100.50.format('$', 'en-us') // '$100.50' - * 100.50.format('$:EUR;$d:code', 'en-us') // 'EUR 100.50' - * ``` - */ - format(pattern?: string, locale?: CanArray): string; - - /** - * Returns a string representation of the number by the specified options - * - * @param opts - formatting options - * @param [locale] - locale for internalizing - */ - format(opts: Intl.NumberFormatOptions, locale?: CanArray): string; - - /** - * Returns a string representation of the number with adding some extra formatting - * - * @deprecated - * @param [length] - length of the decimal part - */ - format(length: number): string; - - /** - * Shortcut for Math.floor that also allows a precision - * @param [precision] - */ - floor(precision?: number): number; - - /** - * Shortcut for Math.round that also allows a precision - * @param [precision] - */ - round(precision?: number): number; - - /** - * Shortcut for Math.ceil that also allows a precision - * @param [precision] - */ - ceil(precision?: number): number; -} - -type RegExpFlag = '' | 'g' | 'i' | 'm' | 'u' | 'y' | 's'; - -interface RegExp { - /** - * Returns a new RegExp based on the source with adding the specified flags - * @param flags - */ - addFlags(...flags: RegExpFlag[]): RegExp; - - /** - * Returns a new RegExp based on the source with removing the specified flags - * @param flags - */ - removeFlags(...flags: RegExpFlag[]): RegExp; - - /** - * Returns a new RegExp based on the source with setting the specified flags - * @param flags - */ - setFlags(...flags: RegExpFlag[]): RegExp; -} - -interface RegExpConstructor { - /** - * Returns the specified value as a string with escaping of all RegExp specific characters - * @param value - */ - escape(value: string): string; - - /** - * Returns true if the specified string is matched with a RegExp. - * If the RegExp has the global flag, it will be ignored. - * - * @param rgxp - * @param str - */ - test(rgxp: RegExp, str: string): boolean; - - /** - * Returns a curried version of `RegExp.test` - * @param rgxp - */ - test(rgxp: RegExp): (str: string) => boolean; - - /** - * Returns a curried version of `inverted` `RegExp.test` - * @param str - */ - test(str: string): (rgxp: RegExp) => boolean; - - /** - * Returns a new RegExp based on the source with adding the specified flags - * - * @param rgxp - * @param flags - */ - addFlags(rgxp: RegExp, ...flags: RegExpFlag[]): RegExp; - - /** - * Returns a curried version of `RegExp.addFlags` - * @param rgxp - */ - addFlags(rgxp: RegExp): (...flags: RegExpFlag[]) => RegExp; - - /** - * Returns a curried version of `inverted` RegExp.addFlags - * @param flags - */ - addFlags(flags: RegExpFlag): (rgxp: RegExp) => RegExp; - - /** - * Returns a new RegExp based on the source with removing the specified flags - * - * @param rgxp - * @param flags - */ - removeFlags(rgxp: RegExp, ...flags: RegExpFlag[]): RegExp; - - /** - * Returns a curried version of `RegExp.removeFlags` - * @param rgxp - */ - removeFlags(rgxp: RegExp): (...flags: RegExpFlag[]) => RegExp; - - /** - * Returns a curried version of `inverted` RegExp.removeFlags - * @param flags - */ - removeFlags(flags: RegExpFlag): (rgxp: RegExp) => RegExp; - - /** - * Returns a new RegExp based on the source with setting the specified flags - * - * @param rgxp - * @param flags - */ - setFlags(rgxp: RegExp, ...flags: RegExpFlag[]): RegExp; - - /** - * Returns a curried version of `RegExp.setFlags` - * @param rgxp - */ - setFlags(rgxp: RegExp): (...flags: RegExpFlag[]) => RegExp; - - /** - * Returns a curried version of `inverted` RegExp.setFlags - * @param flags - */ - setFlags(flags: RegExpFlag): (rgxp: RegExp) => RegExp; -} - -type DateCreateValue = - number | - string | - Date; - -interface DateCreateOptions { - -} - -interface DateConstructor { - /** - * Creates a date from the specified pattern. - * This method can create a new date object from: - * - * 1. another date object - * 1. number of milliseconds (if the number is integer) - * 1. number of seconds (if the number is float) - * 1. string pattern by using the native `Date.parse` with some polyfills - * 1. string aliases: - * 1. `'now'` - is an alias for the now date - * 1. `'today'` - is an alias for the beginning of the today - * 1. `'yesterday'` - is an alias for the beginning of the yesterday - * 1. `'tomorrow'` - is an alias for the beginning of the tomorrow - * - * @param [pattern] - * @param [opts] - * - * @example - * ```js - * Date.create('now'); // new Date(Date.now()) - * Date.create(Date.now()); // new Date(Date.now()) - * ``` - */ - create(pattern?: DateCreateValue, opts?: DateCreateOptions): Date; - - /** - * Returns a list of week days - * @deprecated - */ - getWeekDays(): string[]; - - /** - * Returns a curried version of `Date.is` - * - * @param margin - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - * - * @param date1 - date to compare - */ - is(margin: number, date1: DateCreateValue): (date2: DateCreateValue) => boolean; - - /** - * Returns a curried version of `Date.is` - * @param date1 - date to compare - */ - is(date1: DateCreateValue): (date2: DateCreateValue, margin?: number) => boolean; - - /** - * Returns true if the one date is equals to another - * - * @param date1 - date to compare - * @param date2 - another date to compare - * @param [margin] - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - */ - is(date1: DateCreateValue, date2: DateCreateValue, margin?: number): boolean; - - /** - * Returns a curried version of `Date.isAfter` - * - * @param margin - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - * - * @param date1 - date to compare - */ - isAfter(margin: number, date1: DateCreateValue): (date2: DateCreateValue) => boolean; - - /** - * Returns a curried version of `Date.isAfter` - * @param date1 - date to compare - */ - isAfter(date1: DateCreateValue): (date2: DateCreateValue, margin?: number) => boolean; - - /** - * Returns true if the one date is greater than another - * - * @param date1 - date to compare - * @param date2 - another date to compare - * @param [margin] - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - */ - isAfter(date1: DateCreateValue, date2: DateCreateValue, margin?: number): boolean; - - /** - * Returns a curried version of `Date.isBefore` - * - * @param margin - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - * - * @param date1 - date to compare - */ - isBefore(margin: number, date1: DateCreateValue): (date2: DateCreateValue) => boolean; - - /** - * Returns a curried version of `Date.isBefore` - * @param date1 - date to compare - */ - isBefore(date1: DateCreateValue): (date2: DateCreateValue, margin?: number) => boolean; - - /** - * Returns true if the one date is less than another - * - * @param date1 - date to compare - * @param date2 - another date to compare - * @param [margin] - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - */ - isBefore(date1: DateCreateValue, date2: DateCreateValue, margin?: number): boolean; - - /** - * Returns a curried version of `Date.isBetween` - * - * @param margin - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - */ - isBetween(margin: number): (date: Date, left?: Date, right?: Date) => boolean; - - /** - * Returns a curried version of `Date.isBetween` - * - * @param [left] - date of the beginning - * @param [right] - date of the ending - */ - isBetween(left?: DateCreateValue, right?: DateCreateValue): - (date: Date, left?: Date, right?: Date, margin?: number) => boolean; - - /** - * Returns true if the date is between of two other (including the bounding dates) - * - * @param date - date to check - * @param left - date of the beginning - * @param right - date of the ending - * @param [margin] - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - */ - isBetween(date: Date, left: DateCreateValue, right: DateCreateValue, margin?: number): boolean; - - /** - * Returns a new date based on the current so that it starts at the beginning of a day - * @param date - */ - beginningOfDay(date: Date): Date; - - /** - * Returns a new date based on the current so that it starts at the ending of a day - * @param date - */ - endOfDay(date: Date): Date; - - /** - * Returns a new date based on the current so that it starts at the beginning of a week - * @param date - */ - beginningOfWeek(date: Date): Date; - - /** - * Returns a new date based on the current so that it starts at the ending of a week - * @param date - */ - endOfWeek(date: Date): Date; - - /** - * Returns a new date based on the current so that it starts at the beginning of a month - * @param date - */ - beginningOfMonth(date: Date): Date; - - /** - * Returns a new date based on the current so that it starts at the ending of a month - * @param date - */ - endOfMonth(date: Date): Date; - - /** - * Returns a new date based on the current so that it starts at the beginning of a year - * @param date - */ - beginningOfYear(date: Date): Date; - - /** - * Returns a new date based on the current so that it starts at the ending of a year - * @param date - */ - endOfYear(date: Date): Date; - - /** - * Returns a curried version of `Date.short` - * @param locale - locale for internalizing - */ - short(locale: CanArray): (date: Date) => string; - - /** - * Returns a short string representation of the date. - * This method is based on the native Intl API. - * - * @param date - * @param [locale] - locale for internalizing - * - * @example - * ```js - * new Date('12/28/2019').short('en-us') // '12/28/2019' - * ``` - */ - short(date: Date, locale?: CanArray): string; - - /** - * Returns a curried version of `Date.medium` - * @param locale - locale for internalizing - */ - medium(locale: CanArray): (date: Date) => string; - - /** - * Returns a medium string representation of the date. - * This method is based on the native Intl API. - * - * @param date - * @param [locale] - locale for internalizing - * - * @example - * ```js - * new Date('12/28/2019').medium('en-us') // 'December 28, 2019' - * ``` - */ - medium(date: Date, locale?: CanArray): string; - - /** - * Returns a curried version of `Date.long` - * @param locale - locale for internalizing - */ - long(locale: CanArray): (date: Date) => string; - - /** - * Returns a long string representation of the date. - * This method is based on the native Intl API. - * - * @param date - * @param [locale] - locale for internalizing - * - * @example - * ```js - * new Date('12/28/2019').long('en-us') // '12/28/2019, 12:00:00 AM' - * ``` - */ - long(date: Date, locale?: CanArray): string; - - /** - * Returns a curried version of `Date.format` - * - * @param pattern - * @param locale - */ - format(pattern: string, locale?: CanArray): (date: Date) => string; - - /** - * Returns a curried version of `Date.format` - * - * @param opts - * @param locale - */ - format(opts: Intl.NumberFormatOptions, locale?: CanArray): (date: Date) => string; - - /** - * Returns a string representation of the date by the specified pattern. - * All pattern directives are based on the native `Intl.DateTimeFormat` options: - * - * 1. `'era'` - * 1. `'year'` - * 1. `'month'` - * 1. `'day'` - * 1. `'weekday'` - * 1. `'hour'` - * 1. `'minute'` - * 1. `'second'` - * 1. `'timeZoneName'` - * - * There are aliases for all directives: - * - * 1. `'e'` - era - * 1. `'Y'` - year - * 1. `'M'` - month - * 1. `'d'` - day - * 1. `'w'` - weekday - * 1. `'h'` - hour - * 1. `'m'` - minute - * 1. `'s'` - second - * 1. `'z'` - timeZoneName - * - * Also, some directives support optional use. - * To mark a directive as optional, add the special `?` character after the name or alias. - * - * ```js - * // Will be shown only the day value, - * // because the rest values are equal with `Date.now()` - * new Date().format('year?;month?:short;day', 'en-us'); - * - * // Will be shown all values that are declared in the pattern - * new Date('12/28/2019').format('year?:2-digit;month?;day', 'en-us'); - * ``` - * - * @param date - * @param pattern - string pattern of the format: - * - * 1. symbol `';'` is used as a separator character for pattern directives, for example: `'year;month'` - * 1. symbol `':'` is used for specifying a custom value for a pattern directive, for example: - * `'year:2-digit;month:short'` - * - * @param [locale] - locale for internalizing - * - * @example - * ```js - * new Date('12/28/2019').format('year', 'en-us') // '2019' - * new Date('12/28/2019').format('year:2-digit', 'en-us') // '19' - * new Date('12/28/2019').format('year:2-digit;month', 'en-us') // 'Dec 19' - * - * // Formatting a date by using short aliases - * new Date('12/28/2019').format('Y:2-digit;M:long;d', 'en-us') // 'December 28, 19' - * ``` - */ - format(date: Date, pattern: string, locale?: CanArray): string; - - /** - * Returns a string representation of the date by the specified options - * - * @param date - * @param opts - formatting options - * @param [locale] - locale for internalizing - */ - format(date: Date, opts: Intl.DateTimeFormatOptions, locale?: CanArray): string; - - /** - * Returns a curried version of `Date.toHTMLDateString` - * @param opts - additional options - */ - toHTMLDateString(opts: DateHTMLDateStringOptions): (date: Date) => string; - - /** - * Returns an HTML string representation of the date (without time). - * This method is useful for providing date values within HTML tag attributes. - * - * @param date - * @param [opts] - additional options - */ - toHTMLDateString(date: Date, opts?: DateHTMLDateStringOptions): string; - - /** - * Returns a curried version of `Date.toHTMLTimeString` - * @param opts - additional options - */ - toHTMLTimeString(opts: DateHTMLDateStringOptions): (date: Date) => string; - - /** - * Returns an HTML string representation of a timestamp from the date. - * This method is useful for providing timestamp values within HTML tag attributes. - * - * @param date - * @param [opts] - additional options - */ - toHTMLTimeString(date: Date, opts?: DateHTMLTimeStringOptions): string; - - /** - * Returns a curried version of `Date.toHTMLString` - * @param opts - additional options - */ - toHTMLString(opts: DateHTMLDateStringOptions): (date: Date) => string; - - /** - * Returns an HTML string representation of a datetime from the date. - * This method is useful for providing datetime values within HTML tag attributes. - * - * @param date - * @param [opts] - additional options - */ - toHTMLString(date: Date, opts?: DateHTMLStringOptions): string; - - /** - * Returns a curried version of ``Date.add`` - * - * @param units - * @param reset - if true, then all lower units will be reset to zero - */ - add(units: DateSetParams, reset?: boolean): (date: Date) => Date; - - /** - * Modifies the date with adding time units - * - * @param date - * @param units - * @param reset - if true, then all lower units will be reset to zero - */ - add(date: Date, units: DateSetParams, reset?: boolean): Date; - - /** - * Returns a curried version of `Date.set` - * - * @param units - * @param reset - if true, then all lower units will be reset to zero - */ - set(units: DateSetParams, reset?: boolean): (date: Date) => Date; - - /** - * Modifies the date with setting time units - * - * @param date - * @param units - * @param reset - if true, then all lower units will be reset to zero - */ - set(date: Date, units: DateSetParams, reset?: boolean): Date; - - /** - * Returns a curried version of `Date.rewind` - * - * @param units - * @param reset - if true, then all lower units will be reset to zero - */ - rewind(units: DateSetParams, reset?: boolean): (date: Date) => Date; - - /** - * Modifies the date with subtracting time units - * - * @param date - * @param units - * @param reset - if true, then all lower units will be reset to zero - */ - rewind(date: Date, units: DateSetParams, reset?: boolean): Date; - - /** - * Maps the specified date to the current (Date.now()) date and returns difference - * @param date - */ - relative(date: DateCreateValue): DateRelative; - - /** - * Returns a curried version of `Date.relativeTo` - * @param from - */ - relativeTo(from: DateCreateValue): (to: DateCreateValue) => DateRelative; - - /** - * Maps the one date to another and returns difference - * - * @param from - * @param to - */ - relativeTo(from: DateCreateValue, to: DateCreateValue): DateRelative; -} - -interface DateSetParams { - millisecond?: number; - milliseconds?: number; - second?: number; - seconds?: number; - minute?: number; - minutes?: number; - hour?: number; - hours?: number; - day?: number; - days?: number; - month?: number; - months?: number; - year?: number; - years?: number; -} - -interface DateHTMLDateStringOptions { - /** - * If false, then a date month is taken from the beginning of the now year - * @default `true` - */ - month?: boolean; - - /** - * If false, then a date day is taken from the beginning of the now month - * @default `true` - */ - date?: boolean; -} - -interface DateHTMLTimeStringOptions { - /** - * If false, then a date month is taken from the beginning of the now hour - * @default `true` - */ - minutes?: boolean; - - /** - * If false, then a date second is taken from the beginning of the now minute - * @default `true` - */ - seconds?: boolean; - - /** - * If false, then a date millisecond is taken from the beginning of the now second - * @default `true` - */ - milliseconds?: boolean; -} - -type DateHTMLStringOptions = - DateHTMLTimeStringOptions & - DateHTMLDateStringOptions; - -type DateRelativeType = - 'milliseconds' | - 'seconds' | - 'minutes' | - 'hours' | - 'days' | - 'weeks' | - 'months' | - 'years'; - -interface DateRelative { - type: DateRelativeType; - value: number; - diff: number; -} - -interface Date { - /** - * Clones the date object and returns a new object - */ - clone(): Date; - - /** - * Returns true if the date is equals to another - * - * @param date - another date to compare - * @param [margin] - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - */ - is(date: DateCreateValue, margin?: number): boolean; - - /** - * Returns true if the date is greater than another - * - * @param date - another date to compare - * @param [margin] - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - */ - isAfter(date: DateCreateValue, margin?: number): boolean; - - /** - * Returns true if the date is less than another - * - * @param date - another date to compare - * @param [margin] - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - */ - isBefore(date: DateCreateValue, margin?: number): boolean; - - /** - * Returns true if the date is between of two other (including the bounding dates) - * - * @param left - date of the beginning - * @param right - date of the ending - * @param [margin] - value of the maximum difference between two dates at which they are considered equal - * (in milliseconds) - */ - isBetween(left: DateCreateValue, right: DateCreateValue, margin?: number): boolean; - - /** - * Returns true if the date is less than the now date - */ - isPast(): boolean; - - /** - * Returns true if the date is greater than the now date - */ - isFuture(): boolean; - - /** - * Returns a new date based on the current so that it starts at the beginning of a day - */ - beginningOfDay(): Date; - - /** - * Returns a new date based on the current so that it starts at the ending of a day - */ - endOfDay(): Date; - - /** - * Returns a new date based on the current so that it starts at the beginning of a week - */ - beginningOfWeek(): Date; - - /** - * Returns a new date based on the current so that it starts at the ending of a week - */ - endOfWeek(): Date; - - /** - * Returns a new date based on the current so that it starts at the beginning of a month - */ - beginningOfMonth(): Date; - - /** - * Returns a new date based on the current so that it starts at the ending of a month - */ - endOfMonth(): Date; - - /** - * Returns a new date based on the current so that it starts at the beginning of a year - */ - beginningOfYear(): Date; - - /** - * Returns a new date based on the current so that it starts at the ending of a year - */ - endOfYear(): Date; - - /** - * Returns a short string representation of the date. - * This method is based on the native Intl API. - * - * @param [locale] - locale for internalizing - * - * @example - * ```js - * new Date('12/28/2019').short('en-us') // '12/28/2019' - * ``` - */ - short(locale?: CanArray): string; - - /** - * Returns a medium string representation of the date. - * This method is based on the native Intl API. - * - * @param [locale] - locale for internalizing - * - * @example - * ```js - * new Date('12/28/2019').medium('en-us') // 'December 28, 2019' - * ``` - */ - medium(locale?: CanArray): string; - - /** - * Returns a long string representation of the date. - * This method is based on the native Intl API. - * - * @param [locale] - locale for internalizing - * - * @example - * ```js - * new Date('12/28/2019').long('en-us') // '12/28/2019, 12:00:00 A' - * ``` - */ - long(locale?: CanArray): string; - - /** - * Returns a string representation of the date by the specified pattern. - * All pattern directives are based on the native `Intl.DateTimeFormat` options: - * - * 1. `'era'` - * 1. `'year'` - * 1. `'month'` - * 1. `'day'` - * 1. `'weekday'` - * 1. `'hour'` - * 1. `'minute'` - * 1. `'second'` - * 1. `'timeZoneName'` - * - * There are aliases for all directives: - * - * 1. `'e'` - era - * 1. `'Y'` - year - * 1. `'M'` - month - * 1. `'d'` - day - * 1. `'w'` - weekday - * 1. `'h'` - hour - * 1. `'m'` - minute - * 1. `'s'` - second - * 1. `'z'` - timeZoneName - * - * Also, some directives support optional use. - * To mark a directive as optional, add the special `?` character after the name or alias. - * - * ```js - * // Will be shown only the day value, - * // because the rest values are equal with `Date.now()` - * new Date().format('year?;month?:short;day', 'en-us'); - * - * // Will be shown all values that are declared in the pattern - * new Date('12/28/2019').format('year?:2-digit;month?;day', 'en-us'); - * ``` - * - * @param pattern - string pattern of the format: - * - * 1. symbol `';'` is used as a separator character for pattern directives, for example: `'year;month'` - * 1. symbol `':'` is used for specifying a custom value for a pattern directive, for example: - * `'year:2-digit;month:short'` - * - * @param [locale] - locale for internalizing - * - * @example - * ```js - * new Date('12/28/2019').format('year', 'en-us') // '2019' - * new Date('12/28/2019').format('year:2-digit', 'en-us') // '19' - * new Date('12/28/2019').format('year:2-digit;month', 'en-us') // 'Dec 19' - * - * // Formatting a date by using short aliases - * new Date('12/28/2019').format('Y:2-digit;M:long;d', 'en-us') // 'December 28, 19' - * ``` - */ - format(pattern: string, locale?: CanArray): string; - - /** - * Returns a string representation of the date by the specified options - * - * @param opts - formatting options - * @param [locale] - locale for internalizing - */ - format(opts: Intl.DateTimeFormatOptions, locale?: CanArray): string; - - /** - * Returns an HTML string representation of the date (without time). - * This method is useful for providing date values within HTML tag attributes. - * - * @param [opts] - additional options - */ - toHTMLDateString(opts?: DateHTMLDateStringOptions): string; - - /** - * Returns an HTML string representation of a timestamp from the date. - * This method is useful for providing timestamp values within HTML tag attributes. - * - * @param [opts] - additional options - */ - toHTMLTimeString(opts?: DateHTMLTimeStringOptions): string; - - /** - * Returns an HTML string representation of a datetime from the date. - * This method is useful for providing datetime values within HTML tag attributes. - * - * @param [opts] - additional options - */ - toHTMLString(opts?: DateHTMLStringOptions): string; - - /** - * Modifies the date with adding time units. - * The method mutates the original date. - * - * @param units - * @param reset - if true, then all lower units will be reset to zero - */ - add(units: DateSetParams, reset?: boolean): Date; - - /** - * Modifies the date with setting time units. - * The method mutates the original date. - * - * @param units - * @param reset - if true, then all lower units will be reset to zero - */ - set(units: DateSetParams, reset?: boolean): Date; - - /** - * Modifies the date with subtracting time units. - * The method mutates the original date. - * - * @param units - * @param reset - if true, then all lower units will be reset to zero - */ - rewind(units: DateSetParams, reset?: boolean): Date; - - /** - * Returns a relative value of the date for the now date - */ - relative(): DateRelative; - - /** - * Returns a relative value of the date for another date - * @param date - another date to compare - */ - relativeTo(date: DateCreateValue): DateRelative; - - /** - * Returns a number of days in the date month - */ - daysInMonth(): number; -} - -interface ThrottleOptions { - /** - * Delay to wait in milliseconds - * @default `250` - */ - delay?: number; - - /** - * If true, then all rest invokes that caught in the sleep span are ignored - * @default `false` - */ - single?: boolean; -} - -interface FunctionConstructor { - /** - * Link to the special functional placeholder that can be used with curried functions - * - * @example - * ```js - * function sum(a, b) { - * return a + b; - * } - * - * sum.curry()(Function.__, 2)(5) - * ``` - */ - __: TB.__; - - /** - * Returns a new function that allows to invoke the specified function only once - * @param fn - */ - once(fn: T): T; - - /** - * Returns a new function that allows to invoke a function, which it takes, only with the specified delay. - * The next invocation of the function will cancel the previous. - * - * @param delay - */ - debounce(delay: number): (fn: AnyFunction) => AnyFunction; - - /** - * Returns a new function that allows to invoke the function only with the specified delay. - * The next invocation of the function will cancel the previous. - * - * @param fn - * @param [delay] - */ - debounce(fn: AnyFunction, delay?: number): AnyFunction; - - /** - * Returns a new function that allows to invoke a function, which it takes, not more often than the specified delay. - * The first invoking of a function will run immediately, but all rest invokes will be merged to one and - * executes after the specified delay. - * - * @param delay - */ - throttle(delay: number): (fn: AnyFunction) => AnyFunction; - - /** - * Returns a new function that allows to invoke a function, which it takes, not more often than the specified delay. - * The first invoking of a function will run immediately, but all rest invokes will be merged to one and - * executes after the specified delay. - * - * @param opts - options for the operation - */ - throttle(opts: ThrottleOptions): (fn: AnyFunction) => AnyFunction; - - /** - * Returns a new function that allows to invoke the function not more often than the specified delay. - * The first invoking of a function will run immediately, but all rest invokes will be merged to one and - * executes after the specified delay. - * - * @param fn - * @param [delay] - */ - throttle(fn: AnyFunction, delay?: number): AnyFunction; - - /** - * Returns a new function that allows to invoke the function not more often than the specified delay. - * The first invoking of a function will run immediately, but all rest invokes will be merged to one and - * executes after the specified delay. - * - * @param fn - * @param opts - options for the operation - */ - throttle(fn: AnyFunction, opts: ThrottleOptions): AnyFunction; - - /** - * Returns a curried equivalent of the provided function. - * - * The curried function has two unusual capabilities. - * First, its arguments needn't be provided one at a time. - * If f is a ternary function and g is Function.curry(f), the following are equivalent: - * - * ```js - * g(1)(2)(3) - * g(1)(2, 3) - * g(1, 2)(3) - * g(1, 2, 3) - * ``` - * - * Secondly, the special placeholder value Function.__ may be used to specify "gaps", allowing partial application - * of any combination of arguments, regardless of their positions. If g is as above and _ is Function.__, - * the following are equivalent: - * - * ```js - * g(1, 2, 3) - * g(_, 2, 3)(1) - * g(_, _, 3)(1)(2) - * g(_, _, 3)(1, 2) - * g(_, 2)(1)(3) - * g(_, 2)(1, 3) - * g(_, 2)(_, 3)(1) - * ``` - */ - curry(f: T): TB.Curry; - - /** - * Performs right-to-left function composition. - * The last argument may have any arity; the remaining arguments must be unary. - * - * If any function from parameters returns a Promise, the next function from the parameters - * will take the resolved value of that promise, - * the final result of calling the composition function is also a promise. - * - * @param fn0 - */ - compose(fn0: AnyFunction): AnyFunction; - - compose( - fn1: AnyOneArgFunction, T2>, - fn0: AnyFunction - ): AnyFunction ? NewPromise : T2>; - - compose( - fn2: AnyOneArgFunction, T3>, - fn1: AnyOneArgFunction, T2>, - fn0: AnyFunction - ): AnyFunction ? Promise : T1 extends Promise ? Promise : T3>; - - compose( - fn3: AnyOneArgFunction, T4>, - fn2: AnyOneArgFunction, T3>, - fn1: AnyOneArgFunction, T2>, - fn0: AnyFunction - ): AnyFunction< - A, - T3 extends Promise ? - Promise : T2 extends Promise ? - Promise : T1 extends Promise ? - Promise : T4 - >; - - compose( - fn4: AnyOneArgFunction, T5>, - fn3: AnyOneArgFunction, T4>, - fn2: AnyOneArgFunction, T3>, - fn1: AnyOneArgFunction, T2>, - fn0: AnyFunction - ): AnyFunction< - A, - T4 extends Promise ? - Promise : T3 extends Promise ? - Promise : T2 extends Promise ? - Promise : T1 extends Promise ? - Promise : T5 - >; - - compose( - fn5: AnyOneArgFunction, T6>, - fn4: AnyOneArgFunction, T5>, - fn3: AnyOneArgFunction, T4>, - fn2: AnyOneArgFunction, T3>, - fn1: AnyOneArgFunction, T2>, - fn0: AnyFunction - ): AnyFunction< - A, - T5 extends Promise ? - Promise : T4 extends Promise ? - Promise : T3 extends Promise ? - Promise : T2 extends Promise ? - Promise : T1 extends Promise ? - Promise : T6 - >; -} - -interface Function { - /** - * Returns a new function that allows to invoke the target function only once - */ - once(this: T): T; - - /** - * Returns a new function that allows to invoke the target function only with the specified delay. - * The next invocation of the function will cancel the previous. - * - * @param [delay] - */ - debounce(this: T, delay?: number): AnyFunction, void>; - - /** - * Returns a new function that allows to invoke the target function not more often than the specified delay. - * The first invoking of a function will run immediately, but all rest invokes will be merged to one and - * executes after the specified delay. - * - * @param [delay] - */ - throttle(this: T, delay?: number): AnyFunction, void>; - - /** - * Returns a new function that allows to invoke the target function not more often than the specified delay. - * The first invoking of a function will run immediately, but all rest invokes will be merged to one and - * executes after the specified delay. - * - * @param opts - options for the operation - */ - throttle(this: T, opts: ThrottleOptions): AnyFunction, void>; - - /** - * Returns a new function based on the target that wraps the returning value into the Either structure. - * If the first argument of the created function is taken null or undefined, the function returns the rejected value. - * - * @example - * ```typescript - * function toLowerCase(str: string): string { - * return str.toLowerCase(); - * } - * - * toLowerCase.option()(null).catch((err) => err === null); - * toLowerCase.option()(1).catch((err) => err.message === 'str.toLowerCase is not a function'); - * - * toLowerCase.option()('FOO').then((value) => value === 'foo'); - * toLowerCase.option()(toLowerCase.option()('FOO')).then((value) => value === 'foo'); - * ``` - */ - option(this: () => R): AnyFunction>; - - /** - * Returns a new function based on the target that wraps the returning value into the Either structure. - * If the first argument of the created function is taken null or undefined, the function returns the rejected value. - * - * @example - * ```typescript - * function toLowerCase(str: string): string { - * return str.toLowerCase(); - * } - * - * toLowerCase.option()(null).catch((err) => err === null); - * toLowerCase.option()(1).catch((err) => err.message === 'str.toLowerCase is not a function'); - * - * toLowerCase.option()('FOO').then((value) => value === 'foo'); - * toLowerCase.option()(toLowerCase.option()('FOO')).then((value) => value === 'foo'); - * ``` - */ - option(this: (a1: A1, ...rest: A) => R): - (a1: Maybe> | Either | Nullable, ...rest: A) => Maybe; - - /** - * Returns a new function based on the target that wraps the returning value into the Either structure - * - * @example - * ```typescript - * function toLowerCase(str: string): string { - * return str.toLowerCase(); - * } - * - * toLowerCase.result()(1).catch((err) => err.message === 'str.toLowerCase is not a function'); - * toLowerCase.result()('FOO').then((value) => value === 'foo'); - * toLowerCase.result()(toLowerCase.result()('FOO')).then((value) => value === 'foo'); - * ``` - */ - result(this: () => R): AnyFunction>; - - /** - * Returns a new function based on the target that wraps the returning value into the Either structure - * - * @example - * ```typescript - * function toLowerCase(str: string): string { - * return str.toLowerCase(); - * } - * - * toLowerCase.result()(1).catch((err) => err.message === 'str.toLowerCase is not a function'); - * toLowerCase.result()('FOO').then((value) => value === 'foo'); - * toLowerCase.result()(toLowerCase.result()('FOO')).then((value) => value === 'foo'); - * ``` - */ - result(this: (a1: A1, ...rest: A) => R): - (a1: Maybe | Either, ...rest: A) => Either; - - /** - * Returns a curried equivalent of the function. - * - * The curried function has two unusual capabilities. - * First, its arguments needn't be provided one at a time. - * If f is a ternary function and g is f.curry(), the following are equivalent: - * - * ```js - * g(1)(2)(3) - * g(1)(2, 3) - * g(1, 2)(3) - * g(1, 2, 3) - * ``` - * - * Secondly, the special placeholder value Function.__ may be used to specify "gaps", allowing partial application - * of any combination of arguments, regardless of their positions. If g is as above and _ is Function.__, - * the following are equivalent: - * - * ```js - * g(1, 2, 3) - * g(_, 2, 3)(1) - * g(_, _, 3)(1)(2) - * g(_, _, 3)(1, 2) - * g(_, 2)(1)(3) - * g(_, 2)(1, 3) - * g(_, 2)(_, 3)(1) - * ``` - */ - curry(this: T): TB.Curry; - - /** - * Performs left-to-right function composition. - * The first argument may have any arity; the remaining arguments must be unary. - * - * If any function from parameters returns a Promise, the next function from the parameters - * will take the resolved value of that promise, - * the final result of calling the composition function is also a promise. - */ - compose(this: T): T; - - compose( - this: AnyFunction, - fn1: AnyOneArgFunction - ): AnyFunction ? Promise : T2>; - - compose( - this: AnyFunction, - fn1: AnyOneArgFunction, - fn2: AnyOneArgFunction - ): AnyFunction ? Promise : T1 extends Promise ? Promise : T3>; - - compose( - this: AnyFunction, - fn1: AnyOneArgFunction, - fn2: AnyOneArgFunction, - fn3: AnyOneArgFunction - ): AnyFunction< - A, - T3 extends Promise ? - Promise : T2 extends Promise ? - Promise : T1 extends Promise ? - Promise : T4 - >; - - compose( - this: AnyFunction, - fn1: AnyOneArgFunction, - fn2: AnyOneArgFunction, - fn3: AnyOneArgFunction, - fn4: AnyOneArgFunction - ): AnyFunction< - A, - T4 extends Promise ? - Promise : T3 extends Promise ? - Promise : T2 extends Promise ? - Promise : T1 extends Promise ? - Promise : T5 - >; - - compose( - this: AnyFunction, - fn1: AnyOneArgFunction, - fn2: AnyOneArgFunction, - fn3: AnyOneArgFunction, - fn4: AnyOneArgFunction, - fn5: AnyOneArgFunction - ): AnyFunction< - A, - T5 extends Promise ? - Promise : T4 extends Promise ? - Promise : T3 extends Promise ? - Promise : T2 extends Promise ? - Promise : T1 extends Promise ? - Promise : T6 - >; -} +/// +/// +/// diff --git a/jest.config.ts b/jest.config.ts index 7ceb10f3d..39717842e 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -9,15 +9,14 @@ /** @type {import('ts-jest').InitialOptionsTsJest} */ export default { projects: [''], - testMatch: ['/dist/server/**/*[sS]pec.js', '/src/**/*[sS]pec.ts'], + testMatch: ['/dist/server/**/*[sS]pec.js', '/src/**/*[sS]pec.ts', '/src/**/?(*.)+(spec).ts'], rootDir: './', testTimeout: 20000, testEnvironment: 'node', - bail: 2, reporters: ['default'], - silent: true, clearMocks: true, + silent: true, collectCoverage: true, coverageReporters: ['lcov'], diff --git a/lib/config/index.d.ts b/lib/config/index.d.ts new file mode 100644 index 000000000..5d1a004a1 --- /dev/null +++ b/lib/config/index.d.ts @@ -0,0 +1,17 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +import type { Config } from '../config/interface'; +export * from '../config/interface'; +export declare const $$: StrictDictionary; +declare const config: Config; +/** + * Extends the config object with additional objects + * @param objects + */ +export declare function extend(...objects: Array>): T; +export default config; diff --git a/lib/config/index.js b/lib/config/index.js index 43d6f5b62..9ed2ca0d1 100644 --- a/lib/config/index.js +++ b/lib/config/index.js @@ -45,6 +45,15 @@ const config = { set locale(value) { this[$$.locale] = value; }, + get region() { + if ($$.region in this) { + return this[$$.region]; + } + return typeof REGION !== 'undefined' ? REGION : undefined; + }, + set region(value) { + this[$$.region] = value; + }, get api() { if ($$.api in this) { return this[$$.api]; diff --git a/lib/config/interface.d.ts b/lib/config/interface.d.ts new file mode 100644 index 000000000..f8754839e --- /dev/null +++ b/lib/config/interface.d.ts @@ -0,0 +1,46 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +import type { LogConfig } from '../core/log'; +import type { PerfConfig } from '../core/perf/config'; +import type { OnlineCheckConfig } from '../core/net'; +import type { KVStorageConfig } from '../core/kv-storage/engines/node-localstorage'; +export interface Config { + /** + * Base application name + */ + appName: CanUndef; + /** + * Default system locale + * (used for internalizing) + */ + locale: CanUndef; + /** + * Default system region + */ + region: CanUndef; + /** + * Base API URL: primary service domain + */ + api: CanUndef; + /** + * Options for the "core/kv-storage" module + */ + kvStorage: KVStorageConfig; + /** + * Options for the "core/log" module + */ + log: LogConfig; + /** + * Options for "core/perf" module + */ + perf: PerfConfig; + /** + * Options for the "core/net" module + */ + online: OnlineCheckConfig; +} diff --git a/lib/core/analytics/engines/index.d.ts b/lib/core/analytics/engines/index.d.ts new file mode 100644 index 000000000..5ed28470d --- /dev/null +++ b/lib/core/analytics/engines/index.d.ts @@ -0,0 +1,14 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +import type { AnalyticEngine } from '../../../core/analytics/interface'; +/** + * Sends the specified analytic event + * @abstract + */ +declare const sendEvent: AnalyticEngine; +export default sendEvent; diff --git a/lib/core/analytics/index.d.ts b/lib/core/analytics/index.d.ts new file mode 100644 index 000000000..dd3a71e82 --- /dev/null +++ b/lib/core/analytics/index.d.ts @@ -0,0 +1,12 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +export * from '../../core/analytics/interface'; +/** + * Sends the specified analytic event + */ +export declare function send(...args: unknown[]): void; diff --git a/lib/core/analytics/interface.d.ts b/lib/core/analytics/interface.d.ts new file mode 100644 index 000000000..a8498de06 --- /dev/null +++ b/lib/core/analytics/interface.d.ts @@ -0,0 +1,13 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +/** + * Engine to send analytic events + */ +export interface AnalyticEngine { + (...args: unknown[]): unknown; +} diff --git a/lib/core/async/const.d.ts b/lib/core/async/const.d.ts new file mode 100644 index 000000000..e15d2b4b1 --- /dev/null +++ b/lib/core/async/const.d.ts @@ -0,0 +1,55 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +export declare enum Namespaces { + proxy = 0, + promise = 1, + iterable = 2, + request = 3, + idleCallback = 4, + timeout = 5, + interval = 6, + immediate = 7, + worker = 8, + eventListener = 9, + animationFrame = 10, + proxyPromise = 11, + timeoutPromise = 12, + intervalPromise = 13, + immediatePromise = 14, + idleCallbackPromise = 15, + animationFramePromise = 16, + eventListenerPromise = 17, + length = 18 +} +export declare const enum PrimitiveNamespaces { + proxy = 0, + promise = 1, + iterable = 2, + request = 3, + idleCallback = 4, + timeout = 5, + interval = 6, + immediate = 7, + worker = 8, + eventListener = 9, + animationFrame = 10, + length = 11 +} +export declare const enum PromiseNamespaces { + first = 10, + proxyPromise = 11, + timeoutPromise = 12, + intervalPromise = 13, + immediatePromise = 14, + idleCallbackPromise = 15, + animationFramePromise = 16, + eventListenerPromise = 17, + length = 18 +} +export declare const usedNamespaces: any[]; +export declare const namespacesCache: any[]; diff --git a/lib/core/async/const.js b/lib/core/async/const.js index aa1d7cd3d..50bc11e03 100644 --- a/lib/core/async/const.js +++ b/lib/core/async/const.js @@ -3,9 +3,60 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.namespaces = exports.linkNamesDictionary = void 0; -var _interface = require("../../core/async/interface"); -const namespaces = Object.convertEnumToDict(_interface.Namespaces), - linkNamesDictionary = namespaces; -exports.linkNamesDictionary = linkNamesDictionary; -exports.namespaces = namespaces; \ No newline at end of file +exports.usedNamespaces = exports.namespacesCache = exports.PromiseNamespaces = exports.PrimitiveNamespaces = exports.Namespaces = void 0; +let Namespaces = function (Namespaces) { + Namespaces[Namespaces["proxy"] = 0] = "proxy"; + Namespaces[Namespaces["promise"] = 1] = "promise"; + Namespaces[Namespaces["iterable"] = 2] = "iterable"; + Namespaces[Namespaces["request"] = 3] = "request"; + Namespaces[Namespaces["idleCallback"] = 4] = "idleCallback"; + Namespaces[Namespaces["timeout"] = 5] = "timeout"; + Namespaces[Namespaces["interval"] = 6] = "interval"; + Namespaces[Namespaces["immediate"] = 7] = "immediate"; + Namespaces[Namespaces["worker"] = 8] = "worker"; + Namespaces[Namespaces["eventListener"] = 9] = "eventListener"; + Namespaces[Namespaces["animationFrame"] = 10] = "animationFrame"; + Namespaces[Namespaces["proxyPromise"] = 11] = "proxyPromise"; + Namespaces[Namespaces["timeoutPromise"] = 12] = "timeoutPromise"; + Namespaces[Namespaces["intervalPromise"] = 13] = "intervalPromise"; + Namespaces[Namespaces["immediatePromise"] = 14] = "immediatePromise"; + Namespaces[Namespaces["idleCallbackPromise"] = 15] = "idleCallbackPromise"; + Namespaces[Namespaces["animationFramePromise"] = 16] = "animationFramePromise"; + Namespaces[Namespaces["eventListenerPromise"] = 17] = "eventListenerPromise"; + Namespaces[Namespaces["length"] = 18] = "length"; + return Namespaces; +}({}); +exports.Namespaces = Namespaces; +let PrimitiveNamespaces = function (PrimitiveNamespaces) { + PrimitiveNamespaces[PrimitiveNamespaces["proxy"] = 0] = "proxy"; + PrimitiveNamespaces[PrimitiveNamespaces["promise"] = 1] = "promise"; + PrimitiveNamespaces[PrimitiveNamespaces["iterable"] = 2] = "iterable"; + PrimitiveNamespaces[PrimitiveNamespaces["request"] = 3] = "request"; + PrimitiveNamespaces[PrimitiveNamespaces["idleCallback"] = 4] = "idleCallback"; + PrimitiveNamespaces[PrimitiveNamespaces["timeout"] = 5] = "timeout"; + PrimitiveNamespaces[PrimitiveNamespaces["interval"] = 6] = "interval"; + PrimitiveNamespaces[PrimitiveNamespaces["immediate"] = 7] = "immediate"; + PrimitiveNamespaces[PrimitiveNamespaces["worker"] = 8] = "worker"; + PrimitiveNamespaces[PrimitiveNamespaces["eventListener"] = 9] = "eventListener"; + PrimitiveNamespaces[PrimitiveNamespaces["animationFrame"] = 10] = "animationFrame"; + PrimitiveNamespaces[PrimitiveNamespaces["length"] = 11] = "length"; + return PrimitiveNamespaces; +}({}); +exports.PrimitiveNamespaces = PrimitiveNamespaces; +let PromiseNamespaces = function (PromiseNamespaces) { + PromiseNamespaces[PromiseNamespaces["first"] = 10] = "first"; + PromiseNamespaces[PromiseNamespaces["proxyPromise"] = 11] = "proxyPromise"; + PromiseNamespaces[PromiseNamespaces["timeoutPromise"] = 12] = "timeoutPromise"; + PromiseNamespaces[PromiseNamespaces["intervalPromise"] = 13] = "intervalPromise"; + PromiseNamespaces[PromiseNamespaces["immediatePromise"] = 14] = "immediatePromise"; + PromiseNamespaces[PromiseNamespaces["idleCallbackPromise"] = 15] = "idleCallbackPromise"; + PromiseNamespaces[PromiseNamespaces["animationFramePromise"] = 16] = "animationFramePromise"; + PromiseNamespaces[PromiseNamespaces["eventListenerPromise"] = 17] = "eventListenerPromise"; + PromiseNamespaces[PromiseNamespaces["length"] = 18] = "length"; + return PromiseNamespaces; +}({}); +exports.PromiseNamespaces = PromiseNamespaces; +const usedNamespaces = new Array(Namespaces.length).fill(false); +exports.usedNamespaces = usedNamespaces; +const namespacesCache = new Array(Namespaces.length).fill(null); +exports.namespacesCache = namespacesCache; \ No newline at end of file diff --git a/lib/core/async/core/api.d.ts b/lib/core/async/core/api.d.ts new file mode 100644 index 000000000..b17864231 --- /dev/null +++ b/lib/core/async/core/api.d.ts @@ -0,0 +1,36 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +import Super from '../../../core/async/core/core'; +import type { ClearOptions } from '../../../core/async/interface'; +export default class Async> extends Super { + /** + * Clears all asynchronous tasks + * @param [opts] - additional options for the operation + */ + clearAll(opts?: ClearOptions): this; + /** + * Mutes all asynchronous tasks + * @param [opts] - additional options for the operation + */ + muteAll(opts?: ClearOptions): this; + /** + * Unmutes all asynchronous tasks + * @param [opts] - additional options for the operation + */ + unmuteAll(opts?: ClearOptions): this; + /** + * Suspends all asynchronous tasks + * @param [opts] - additional options for the operation + */ + suspendAll(opts?: ClearOptions): this; + /** + * Unsuspends all asynchronous tasks + * @param [opts] - additional options for the operation + */ + unsuspendAll(opts?: ClearOptions): this; +} diff --git a/lib/core/async/core/api.js b/lib/core/async/core/api.js new file mode 100644 index 000000000..465525a95 --- /dev/null +++ b/lib/core/async/core/api.js @@ -0,0 +1,79 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _core = _interopRequireDefault(require("../../../core/async/core/core")); +var _const = require("../../../core/async/core/const"); +class Async extends _core.default { + clearAll(opts) { + this.usedNamespaces.forEach((used, i) => { + if (!used) { + return; + } + const key = this.Namespaces[i], + alias = `clear-${key}`.camelize(false); + if (Object.isFunction(this[alias])) { + this[alias](opts); + } else if (!_const.isPromisifyNamespace.test(i)) { + throw new ReferenceError(`The method "${alias}" is not defined`); + } + }); + return this; + } + muteAll(opts) { + this.usedNamespaces.forEach((used, i) => { + if (!used) { + return; + } + const key = this.Namespaces[i], + alias = `mute-${key}`.camelize(false); + if (!_const.isPromisifyNamespace.test(i) && Object.isFunction(this[alias])) { + this[alias](opts); + } + }); + return this; + } + unmuteAll(opts) { + this.usedNamespaces.forEach((used, i) => { + if (!used) { + return; + } + const key = this.Namespaces[i], + alias = `unmute-${key}`.camelize(false); + if (!_const.isPromisifyNamespace.test(i) && Object.isFunction(this[alias])) { + this[alias](opts); + } + }); + return this; + } + suspendAll(opts) { + this.usedNamespaces.forEach((used, i) => { + if (!used) { + return; + } + const key = this.Namespaces[i], + alias = `suspend-${key}`.camelize(false); + if (!_const.isPromisifyNamespace.test(i) && Object.isFunction(this[alias])) { + this[alias](opts); + } + }); + return this; + } + unsuspendAll(opts) { + this.usedNamespaces.forEach((used, i) => { + if (!used) { + return; + } + const key = this.Namespaces[i], + alias = `unsuspend-${key}`.camelize(false); + if (!_const.isPromisifyNamespace.test(i) && Object.isFunction(this[alias])) { + this[alias](opts); + } + }); + return this; + } +} +exports.default = Async; \ No newline at end of file diff --git a/lib/core/async/core/const.d.ts b/lib/core/async/core/const.d.ts new file mode 100644 index 000000000..a231ff2f5 --- /dev/null +++ b/lib/core/async/core/const.d.ts @@ -0,0 +1,15 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +import { Namespaces } from '../../../core/async/const'; +export declare const asyncCounter: unique symbol; +export declare const isZombieGroup: { + test(group: string): boolean; +}; +export declare const isPromisifyNamespace: { + test(namespace: Namespaces): boolean; +}; diff --git a/lib/core/async/core/const.js b/lib/core/async/core/const.js new file mode 100644 index 000000000..bc6c51c07 --- /dev/null +++ b/lib/core/async/core/const.js @@ -0,0 +1,21 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.isZombieGroup = exports.isPromisifyNamespace = exports.asyncCounter = void 0; +var _const = require("../../../core/async/const"); +const asyncCounter = Symbol('Async counter id'); +exports.asyncCounter = asyncCounter; +const isZombieGroup = { + test(group) { + return group.includes(':zombie'); + } +}; +exports.isZombieGroup = isZombieGroup; +const isPromisifyNamespace = { + test(namespace) { + return namespace > _const.PromiseNamespaces.first; + } +}; +exports.isPromisifyNamespace = isPromisifyNamespace; \ No newline at end of file diff --git a/lib/core/async/core/core.d.ts b/lib/core/async/core/core.d.ts new file mode 100644 index 000000000..18b52a53b --- /dev/null +++ b/lib/core/async/core/core.d.ts @@ -0,0 +1,83 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +import { Namespaces, PrimitiveNamespaces, PromiseNamespaces } from '../../../core/async/const'; +import type { FullAsyncParams, FullClearParams, ClearProxyOptions, Marker, GlobalCache } from '../../../core/async/interface'; +export default class Async> { + /** + * The enum containing all namespaces supported by Async + */ + static readonly Namespaces: typeof Namespaces; + /** + * The lock status. + * If set to true, all new tasks won't be registered. + */ + locked: boolean; + /** + * The enum containing all namespaces supported by Async + */ + readonly Namespaces: typeof Namespaces; + /** + * Cache for asynchronous operations + */ + protected readonly cache: GlobalCache[]; + /** + * Cache for initialized workers + */ + protected readonly workerCache: WeakMap; + /** + * Task identifiers + */ + protected readonly ids: WeakMap; + /** + * The context of applying for asynchronous handlers + */ + protected readonly ctx: CTX; + /** + * Used asynchronous namespaces + */ + protected readonly usedNamespaces: boolean[]; + /** + * @param [ctx] - the context of applying for asynchronous handlers + */ + constructor(ctx?: CTX); + /** + * Returns a cache object by the specified name + * + * @param task + */ + protected getCache(task: Pick | FullClearParams, 'namespace' | 'promise' | 'label'> & { + group?: string | RegExp; + }): GlobalCache; + /** + * Registers an asynchronous task with the specified parameters + * @param params + */ + protected registerTask(params: FullAsyncParams): R | null; + /** + * Cancels an asynchronous task (or a group of tasks) from the specified namespace + * + * @param params - the operation parameters or a reference to the task to be canceled + * @param [namespace] - the namespace from which the task or tasks should be canceled + */ + protected cancelTask(params: CanUndef, namespace?: Namespaces | PrimitiveNamespaces | PromiseNamespaces): this; + /** + * Marks an asynchronous task (or a group of tasks) within the specified namespace using the given marker + * + * @param marker + * @param params - the operation parameters or a reference to the task to be marked + * @param [namespace] - the namespace from which the task or tasks should be marked + */ + protected markTask(marker: Marker, params: CanUndef, namespace?: Namespaces | PrimitiveNamespaces | PromiseNamespaces): this; + /** + * Marks all asynchronous tasks within the specified namespace using the given marker + * + * @param marker + * @param opts - the operation options + */ + protected markAllTasks(marker: Marker, opts: FullClearParams): this; +} diff --git a/lib/core/async/core/core.js b/lib/core/async/core/core.js new file mode 100644 index 000000000..940ab868c --- /dev/null +++ b/lib/core/async/core/core.js @@ -0,0 +1,419 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _task = _interopRequireDefault(require("../../../core/async/core/task")); +var _const = require("../../../core/async/const"); +var _const2 = require("../../../core/async/core/const"); +class Async { + static Namespaces = _const.Namespaces; + locked = false; + Namespaces = _const.Namespaces; + cache = _const.namespacesCache.slice(); + workerCache = new WeakMap(); + ids = new WeakMap(); + usedNamespaces = _const.usedNamespaces.slice(); + constructor(ctx) { + this.ctx = ctx ?? Object.cast(this); + } + getCache(task) { + const pos = task.promise ?? task.namespace; + const cache = this.cache[pos] ?? { + root: { + labels: null, + links: new Map() + }, + groups: null + }; + if (task.group != null && cache.groups == null) { + cache.groups = Object.createDict(); + } + if (task.label != null && cache.root.labels == null) { + cache.root.labels = Object.createDict(); + } + this.cache[pos] = cache; + return cache; + } + registerTask(params) { + if (this.locked) { + return null; + } + this.usedNamespaces[params.promise != null ? _const.PrimitiveNamespaces.promise : params.namespace] = true; + const commonCache = this.getCache(params); + const { + label, + group + } = params; + let cache; + if (group != null && commonCache.groups != null) { + cache = commonCache.groups[group] ?? { + labels: null, + links: new Map() + }; + if (label != null && cache.labels == null) { + cache.labels = Object.createDict(); + } + commonCache.groups[group] = cache; + } else { + cache = commonCache.root; + } + const { + ctx + } = this, + { + labels, + links + } = cache, + { + links: globalLinks + } = commonCache.root; + const labelCache = label != null && labels != null ? labels[label] : null; + if (labelCache != null && params.join === true) { + const link = links.get(labelCache); + Array.toArray(params.onMerge).forEach(handler => { + handler.call(ctx, link); + }); + return labelCache; + } + const normalizedTask = params.callable && Object.isFunction(params.task) ? params.task.call(ctx) : params.task; + let taskId = normalizedTask, + wrappedTask = normalizedTask, + task = null; + if (!params.periodic || Object.isFunction(wrappedTask)) { + wrappedTask = (...args) => { + if (task == null || task.unregistered) { + return; + } + if (task.muted) { + Array.toArray(params.onMutedCall).forEach(handler => { + handler.call(ctx, task); + }); + } + if (task.muted) { + return; + } + if (!params.periodic) { + if (task.paused) { + task.muted = true; + } else { + task.unregister(); + } + } + const needUnregister = !params.periodic && task.paused; + if (task.paused) { + task.queue.push(execTask); + return; + } + return execTask(); + function execTask() { + if (task == null) { + return; + } + if (needUnregister) { + task.unregister(); + } + let taskRes = normalizedTask; + if (Object.isFunction(normalizedTask)) { + switch (args.length) { + case 0: + taskRes = normalizedTask.call(ctx); + break; + case 1: + taskRes = normalizedTask.call(ctx, args[0]); + break; + case 2: + taskRes = normalizedTask.call(ctx, args[0], args[1]); + break; + case 3: + taskRes = normalizedTask.call(ctx, args[0], args[1], args[2]); + break; + default: + taskRes = normalizedTask.apply(ctx, args); + } + } + if (Object.isPromiseLike(taskRes)) { + taskRes.then(invokeHandlers(), invokeHandlers(1)); + } else { + const handler = invokeHandlers(); + switch (args.length) { + case 0: + handler(); + break; + case 1: + handler(args[0]); + break; + case 2: + handler(args[0], args[1]); + break; + case 3: + handler(args[0], args[1], args[2]); + break; + default: + taskRes = handler(...args); + } + } + return taskRes; + } + function invokeHandlers(i = 0) { + return (...args) => { + if (task == null) { + return; + } + const handlers = task.onComplete; + if (Object.isArray(handlers)) { + handlers.forEach(handler => { + const resolvedHandler = Object.isFunction(handler) ? handler : handler[i]; + switch (args.length) { + case 0: + resolvedHandler.call(ctx); + break; + case 1: + resolvedHandler.call(ctx, args[0]); + break; + case 2: + resolvedHandler.call(ctx, args[0], args[1]); + break; + default: + resolvedHandler.apply(ctx, args); + } + }); + } + }; + } + }; + } + if (params.wrapper) { + const args = Array.toArray(wrappedTask, params.callable ? taskId : null, params.args); + let taskRes; + switch (args.length) { + case 1: + taskRes = params.wrapper.call(null, args[0]); + break; + case 2: + taskRes = params.wrapper.call(null, args[0], args[1]); + break; + case 3: + taskRes = params.wrapper.call(null, args[0], args[1], args[2]); + break; + default: + taskRes = params.wrapper(...args); + } + if (params.linkByWrapper) { + taskId = taskRes; + } + } + task = new _task.default(taskId, params, cache, commonCache); + if (labelCache != null) { + this.cancelTask({ + ...params, + replacedBy: task, + reason: 'collision' + }); + } + links.set(taskId, task); + if (links !== globalLinks) { + globalLinks.set(taskId, task); + } + if (label != null && labels != null) { + labels[label] = taskId; + } + return taskId; + } + cancelTask(params, namespace) { + params = params != null ? this.ids.get(params) ?? params : params; + let p; + if (namespace != null) { + if (params === undefined) { + return this.cancelTask({ + namespace, + reason: 'all' + }); + } + p = Object.isDictionary(params) ? { + ...params, + namespace + } : { + namespace, + id: params + }; + } else { + p = params ?? {}; + } + const commonCache = this.getCache(p); + const { + group, + label + } = p; + let cache; + if (group != null && commonCache.groups != null) { + if (Object.isRegExp(group)) { + Object.keys(commonCache.groups).forEach(groupName => { + if (group.test(groupName)) { + this.cancelTask({ + ...p, + group: groupName, + reason: 'rgxp' + }); + } + }); + return this; + } + const localCache = commonCache.groups[group]; + if (localCache == null) { + return this; + } + cache = localCache; + if (p.reason == null) { + p.reason = 'group'; + } + } else { + cache = commonCache.root; + } + const { + labels, + links + } = cache; + if (label != null && labels != null) { + const id = labels[label]; + if (p.id != null && p.id !== id) { + return this; + } + p.id = id; + if (p.reason == null) { + p.reason = 'label'; + } + } + if (p.reason == null) { + p.reason = 'id'; + } + if (p.id != null) { + const task = links.get(p.id); + if (task != null) { + const skipZombie = task.group != null && p.reason === 'all' && _const2.isZombieGroup.test(task.group); + if (skipZombie) { + return this; + } + task.unregister(); + const ctx = { + ...p, + link: task, + type: 'clearAsync' + }; + task.onClear.forEach(handler => { + handler.call(this.ctx, ctx); + }); + if (task.clear != null && !p.preventDefault) { + task.clear.call(null, task.id, ctx); + } + } + } else { + links.forEach(link => { + this.cancelTask({ + ...p, + id: link.id + }); + }); + } + return this; + } + markTask(marker, params, namespace) { + params = params != null ? this.ids.get(params) ?? params : params; + let p; + if (namespace != null) { + if (params === undefined) { + return this.markTask(marker, { + namespace, + reason: 'all' + }); + } + p = Object.isDictionary(params) ? { + ...params, + namespace + } : { + namespace, + id: params + }; + } else { + p = params ?? {}; + } + const commonCache = this.getCache(p); + const { + group, + label + } = p; + let cache; + if (group != null && commonCache.groups != null) { + if (Object.isRegExp(group)) { + Object.keys(commonCache.groups).forEach(groupName => { + if (group.test(groupName)) { + this.markTask(marker, { + ...p, + group: groupName, + reason: 'rgxp' + }); + } + }); + return this; + } + const localCache = commonCache.groups[group]; + if (localCache == null) { + return this; + } + cache = localCache; + } else { + cache = commonCache.root; + } + const { + labels, + links + } = cache; + if (label != null && labels != null) { + const id = labels[label]; + if (p.id != null && p.id !== id) { + return this; + } + p.id = id; + if (p.reason == null) { + p.reason = 'label'; + } + } + if (p.reason == null) { + p.reason = 'id'; + } + if (p.id != null) { + const task = links.get(p.id); + if (task) { + const skipZombie = task.group != null && p.reason === 'all' && _const2.isZombieGroup.test(task.group); + if (skipZombie) { + return this; + } + if (marker === '!paused') { + task.queue.forEach(fn => fn()); + task.muted = false; + task.paused = false; + task.queue = []; + } else if (marker.startsWith('!')) { + task[marker.slice(1)] = false; + } else { + task[marker] = true; + } + } + } else { + links.forEach(link => { + this.markTask(marker, { + ...p, + id: link.id + }); + }); + } + return this; + } + markAllTasks(marker, opts) { + this.markTask(marker, opts); + return this; + } +} +exports.default = Async; \ No newline at end of file diff --git a/lib/core/async/core/helpers.d.ts b/lib/core/async/core/helpers.d.ts new file mode 100644 index 000000000..b1d3c316d --- /dev/null +++ b/lib/core/async/core/helpers.d.ts @@ -0,0 +1,13 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +import type { AsyncOptions } from '../../../core/async/events'; +/** + * Returns true if the specified value appears to be an instance of AsyncOptions + * @param value + */ +export declare function isAsyncOptions(value: unknown): value is T; diff --git a/lib/core/async/core/helpers.js b/lib/core/async/core/helpers.js new file mode 100644 index 000000000..68771f4db --- /dev/null +++ b/lib/core/async/core/helpers.js @@ -0,0 +1,9 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.isAsyncOptions = isAsyncOptions; +function isAsyncOptions(value) { + return Object.isDictionary(value); +} \ No newline at end of file diff --git a/lib/core/async/core/index.d.ts b/lib/core/async/core/index.d.ts new file mode 100644 index 000000000..a3b60bb7a --- /dev/null +++ b/lib/core/async/core/index.d.ts @@ -0,0 +1,15 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +/** + * [[include:core/async/core/README.md]] + * @packageDocumentation + */ +export { default } from '../../../core/async/core/api'; +export * from '../../../core/async/core/const'; +export * from '../../../core/async/core/helpers'; +export * from '../../../core/async/interface'; diff --git a/lib/core/async/core/index.js b/lib/core/async/core/index.js new file mode 100644 index 000000000..4c06e5f80 --- /dev/null +++ b/lib/core/async/core/index.js @@ -0,0 +1,50 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +var _exportNames = {}; +Object.defineProperty(exports, "default", { + enumerable: true, + get: function () { + return _api.default; + } +}); +var _api = _interopRequireDefault(require("../../../core/async/core/api")); +var _const = require("../../../core/async/core/const"); +Object.keys(_const).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _const[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _const[key]; + } + }); +}); +var _helpers = require("../../../core/async/core/helpers"); +Object.keys(_helpers).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _helpers[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _helpers[key]; + } + }); +}); +var _interface = require("../../../core/async/interface"); +Object.keys(_interface).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _interface[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _interface[key]; + } + }); +}); \ No newline at end of file diff --git a/lib/core/async/core/interface.d.ts b/lib/core/async/core/interface.d.ts new file mode 100644 index 000000000..b8c426ffa --- /dev/null +++ b/lib/core/async/core/interface.d.ts @@ -0,0 +1,205 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +import type { Namespaces, PrimitiveNamespaces, PromiseNamespaces } from '../../../core/async/const'; +import type Async from '../../../core/async'; +export declare type Group = string; +export declare type Label = string | symbol; +export declare type Join = boolean | 'replace'; +/** + * Values by which a task can be marked + */ +export declare type Marker = 'muted' | '!muted' | 'paused' | '!paused'; +/** + * The reason why a task can be marked + */ +export declare type MarkReason = 'id' | 'label' | 'group' | 'rgxp' | 'all'; +/** + * The reason why a task can be killed (cleared) + */ +export declare type ClearReason = MarkReason | 'muting' | 'collision'; +export interface IdObject { + __id__: unknown; +} +export interface AsyncOptions { + /** + * A label of the task. + * Any previous task with the same label will be canceled. + */ + label?: Label; + /** + * A group name of the task + */ + group?: Group; + /** + * A strategy to join competitive tasks (tasks with the same labels): + * 1. `true` - all following tasks are joined to the first task; + * 2. `'replace'` - all following tasks are joined (replaced) to the last one (only for promises). + */ + join?: Join; +} +export declare type ProxyCb = A extends never ? ((this: CTX) => R) : A extends any[] ? ((this: CTX, ...args: A) => R) : ((this: CTX, e: A) => R) | Function; +export declare type AsyncCb = ProxyCb, void, CTX>; +export interface AsyncCbOptions extends AsyncOptions { + /** + * The task namespace for operations when they are used as promisified + * @default `false` + */ + promise?: PromiseNamespaces; + /** + * Handler(s) responsible for task clearing + */ + onClear?: CanArray>; + /** + * Handler(s) of task merging: a task should merge with another task that has + * the same label and a `join: true` strategy + */ + onMerge?: CanArray>; + /** + * Handler(s) of muted task calling. + * These handlers are invoked when a muted task is called. + */ + onMutedCall?: CanArray>; +} +export interface AsyncCbOptionsSingle extends AsyncCbOptions { + /** + * If set to false, the proxy supports multiple calls + * @default `true` + */ + single?: boolean; +} +export declare type TaskNamespaces = Namespaces | PrimitiveNamespaces | PromiseNamespaces; +export interface AsyncProxyOptions extends AsyncCbOptionsSingle { + /** + * The proxy namespace + */ + namespace?: TaskNamespaces; + /** + * A function to clear the proxy memory + */ + clear?: ClearFn; +} +export interface ClearOptions { + /** + * A label of the task to clear + */ + label?: Label; + /** + * A group name of the task to clear + */ + group?: Group | RegExp; + /** + * If set to true, the task's cleanup handler is prevented + */ + preventDefault?: boolean; +} +export interface ClearOptionsId extends ClearOptions { + /** + * The identifier of the task to clear + */ + id?: ID; +} +export interface ClearProxyOptions extends ClearOptionsId { + /** + * The proxy namespace to clear + */ + namespace?: TaskNamespaces; +} +export declare type StrictClearOptions = Omit | Overwrite; +export declare type StrictClearOptionsId = Omit, 'label'> | Overwrite, { + label: Label; + group: Group | RegExp; +}>; +export interface ClearFn extends Function { + (id: any, ctx: TaskCtx): void; +} +export interface BoundFn extends Function { + (this: CTX, ...args: any[]): void; +} +export interface Task { + /** + * The task unique identifier + */ + id: unknown; + /** + * The raw task object + */ + task: unknown; + /** + * The name of the raw task object + */ + name?: string; + /** + * The group name associated with the task + */ + group?: string; + /** + * The label associated with the task + */ + label?: Label; + /** + * True if the task is unregistered + */ + unregistered: boolean; + /** + * True if the task is paused + */ + paused: boolean; + /** + * True if the task is muted + */ + muted: boolean; + /** + * A queue of pending handlers in case the task is paused + */ + queue: Function[]; + /** + * A list of complete handlers: `[onFulfilled, onRejected][]` + */ + onComplete: Array>>; + /** + * A list of clear handlers + */ + onClear: Array>; + /** + * A function to clear the task + */ + clear: CanNull>; + /** + * Unregisters the task + */ + unregister(): void; +} +export declare type TaskCtx = { + /** + * The task type + */ + type: string; + /** + * A link to the registered task + */ + link: Task; + /** + * A link to a new task that replaces the current one + */ + replacedBy?: Task; + /** + * The reason to clear the task + */ + reason?: ClearReason; +} & AsyncOptions & ClearOptionsId; +export interface LocalCache { + labels: CanNull>; + links: Map>; +} +export interface GlobalCache { + root: LocalCache; + groups: CanNull>; +} diff --git a/lib/core/async/modules/base/interface.js b/lib/core/async/core/interface.js similarity index 100% rename from lib/core/async/modules/base/interface.js rename to lib/core/async/core/interface.js diff --git a/lib/core/async/core/task.d.ts b/lib/core/async/core/task.d.ts new file mode 100644 index 000000000..6b81b83a9 --- /dev/null +++ b/lib/core/async/core/task.d.ts @@ -0,0 +1,52 @@ +/*! + * V4Fire Core + * https://github.com/V4Fire/Core + * + * Released under the MIT license + * https://github.com/V4Fire/Core/blob/master/LICENSE + */ +import type Async from '../../../core/async'; +import type { Label, FullAsyncParams, LocalCache, GlobalCache, AsyncCb, BoundFn, ClearFn, Task as AbstractTask } from '../../../core/async/interface'; +export default class Task implements AbstractTask { + /** @inheritDoc */ + readonly id: object; + /** @inheritDoc */ + readonly task: FullAsyncParams['task']; + /** @inheritDoc */ + get name(): CanUndef; + /** @inheritDoc */ + readonly group: CanUndef; + /** @inheritDoc */ + readonly label: CanUndef