Compare commits

..

61 Commits

Author SHA1 Message Date
20182a00f8 v6.3.1
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-03-01 19:21:42 +00:00
ddf4e698c9 fix(classes): cleanup resources, add cancellable timeouts, and fix bugs in several core utility classes 2026-03-01 19:21:42 +00:00
597e9e15c3 v6.3.0
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-03-01 12:29:44 +00:00
03a33195bc feat(tooling): update build tooling, developer dependencies, npmextra configuration, and expand README documentation 2026-03-01 12:29:44 +00:00
47d339bb2b v6.2.3
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-03-01 12:18:16 +00:00
bf3b4e832a fix(interestmap): remove interest from InterestMap immediately after fulfillment 2026-03-01 12:18:16 +00:00
685f4ebb3b 6.2.2 2025-04-25 19:25:25 +00:00
2ff7efe6d8 fix(docs): Update @push.rocks/tapbundle dependency and refine AsyncExecutionStack documentation examples 2025-04-25 19:25:25 +00:00
99072d5fdf 6.2.1 2025-04-25 19:15:23 +00:00
4442ddffcd fix(AsyncExecutionStack tests): Refactor AsyncExecutionStack tests: update non-exclusive concurrency assertions and clean up test logic 2025-04-25 19:15:23 +00:00
7c5b3825ac 6.2.0 2025-04-25 18:57:26 +00:00
84babb3cd4 feat(AsyncExecutionStack): Improve non-exclusive task management with concurrency limit controls and enhanced monitoring in AsyncExecutionStack. 2025-04-25 18:57:26 +00:00
5d9624bd56 6.1.0 2024-10-13 20:40:40 +02:00
32397a97cd feat(BackpressuredArray): Add method to check if items are present in BackpressuredArray 2024-10-13 20:40:40 +02:00
753b829d18 update description 2024-05-29 14:11:13 +02:00
4be2784bf4 6.0.15 2024-04-18 21:55:34 +02:00
cd08cf370c fix(core): update 2024-04-18 21:55:33 +02:00
b64fe567a8 update tsconfig 2024-04-14 13:39:10 +02:00
2a68c9ad90 update npmextra.json: githost 2024-04-01 21:33:17 +02:00
aa978413d1 update npmextra.json: githost 2024-04-01 19:57:26 +02:00
f24636ba80 update npmextra.json: githost 2024-03-30 21:46:23 +01:00
5e31dbb504 6.0.14 2024-02-25 13:01:07 +01:00
b2ca6e13e7 fix(core): update 2024-02-25 13:01:06 +01:00
052322fb98 6.0.13 2024-02-23 17:44:52 +01:00
9e1eb0b3a0 fix(core): update 2024-02-23 17:44:51 +01:00
af9b045d31 6.0.12 2023-11-13 17:20:46 +01:00
d7718d4340 fix(core): update 2023-11-13 17:20:45 +01:00
92592d9e9a 6.0.11 2023-11-13 16:44:39 +01:00
a786c43970 fix(core): update 2023-11-13 16:44:38 +01:00
66658dc877 6.0.10 2023-11-13 16:43:07 +01:00
be78d74124 fix(core): update 2023-11-13 16:43:06 +01:00
bde0404777 6.0.9 2023-11-13 16:27:05 +01:00
dfe973f5d8 fix(core): update 2023-11-13 16:27:04 +01:00
326030456f 6.0.8 2023-11-13 14:59:04 +01:00
184dc98127 fix(core): update 2023-11-13 14:59:03 +01:00
702ce00288 6.0.7 2023-11-13 14:40:02 +01:00
ff0d745170 fix(core): update 2023-11-13 14:40:01 +01:00
8fc0438c75 6.0.6 2023-11-09 21:17:14 +01:00
355c2d132e fix(core): update 2023-11-09 21:17:13 +01:00
c083a47f07 6.0.5 2023-08-14 19:31:21 +02:00
9eb9403bea fix(core): update 2023-08-14 19:31:21 +02:00
029f2a6872 6.0.4 2023-08-14 19:27:29 +02:00
66676d89a5 fix(core): update 2023-08-14 19:27:28 +02:00
775a307056 6.0.3 2023-07-12 10:07:36 +02:00
4aa7621401 fix(core): update 2023-07-12 10:07:35 +02:00
b7ac5cb864 switch to new org scheme 2023-07-11 00:00:55 +02:00
f580281ccd switch to new org scheme 2023-07-10 02:41:33 +02:00
cc66f81c7e 6.0.2 2023-01-18 17:45:20 +01:00
6f1e32284d fix(core): update 2023-01-18 17:45:19 +01:00
47b511f1f0 6.0.1 2023-01-18 12:18:48 +01:00
24f82fe570 fix(core): update 2023-01-18 12:18:47 +01:00
60a0fab9db 6.0.0 2022-05-27 17:58:02 +02:00
61e287fbe4 BREAKING CHANGE(core): switch to esm 2022-05-27 17:58:02 +02:00
984297c9c3 5.0.6 2022-05-27 17:57:14 +02:00
55e4edc785 fix(core): update 2022-05-27 17:57:14 +02:00
0fdc891326 5.0.5 2022-05-27 17:53:02 +02:00
69ab47ed41 fix(core): update 2022-05-27 17:53:02 +02:00
b1bd8132de 5.0.4 2022-01-24 21:54:16 +01:00
4e672b13f4 fix(core): update 2022-01-24 21:54:16 +01:00
75c2f23d02 5.0.3 2022-01-24 07:45:40 +01:00
da0c459d74 fix(core): update 2022-01-24 07:45:38 +01:00
37 changed files with 13387 additions and 28212 deletions

View File

@@ -0,0 +1,66 @@
name: Default (not tags)
on:
push:
tags-ignore:
- '**'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Install pnpm and npmci
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
- name: Run npm prepare
run: npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build

View File

@@ -0,0 +1,124 @@
name: Default (tags)
on:
push:
tags:
- '*'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build
release:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Release
run: |
npmci node install stable
npmci npm publish
metadata:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
continue-on-error: true
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Code quality
run: |
npmci command npm install -g typescript
npmci npm install
- name: Trigger
run: npmci trigger
- name: Build docs and upload artifacts
run: |
npmci node install stable
npmci npm install
pnpm install -g @git.zone/tsdoc
npmci command tsdoc
continue-on-error: true

View File

@@ -1,138 +0,0 @@
# gitzone ci_default
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache:
paths:
- .npmci_cache/
key: '$CI_BUILD_STAGE'
stages:
- security
- test
- release
- metadata
# ====================
# security stage
# ====================
mirror:
stage: security
script:
- npmci git mirror
only:
- tags
tags:
- lossless
- docker
- notpriv
auditProductionDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci npm prepare
- npmci command npm install --production --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=prod --production
tags:
- docker
allow_failure: true
auditDevDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci npm prepare
- npmci command npm install --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=dev
tags:
- docker
allow_failure: true
# ====================
# test stage
# ====================
testStable:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
testBuild:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
release:
stage: release
script:
- npmci node install stable
- npmci npm publish
only:
- tags
tags:
- lossless
- docker
- notpriv
# ====================
# metadata stage
# ====================
codequality:
stage: metadata
allow_failure: true
only:
- tags
script:
- npmci command npm install -g tslint typescript
- npmci npm prepare
- npmci npm install
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
tags:
- lossless
- docker
- priv
trigger:
stage: metadata
script:
- npmci trigger
only:
- tags
tags:
- lossless
- docker
- notpriv
pages:
stage: metadata
script:
- npmci node install lts
- npmci command npm install -g @gitzone/tsdoc
- npmci npm prepare
- npmci npm install
- npmci command tsdoc
tags:
- lossless
- docker
- notpriv
only:
- tags
artifacts:
expire_in: 1 week
paths:
- public
allow_failure: true

24
.vscode/launch.json vendored
View File

@@ -2,28 +2,10 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "current file", "command": "npm test",
"type": "node", "name": "Run npm test",
"request": "launch", "request": "launch",
"args": [ "type": "node-terminal"
"${relativeFile}"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
},
{
"name": "test.ts",
"type": "node",
"request": "launch",
"args": [
"test/test.ts"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
} }
] ]
} }

104
changelog.md Normal file
View File

@@ -0,0 +1,104 @@
# Changelog
## 2026-03-01 - 6.3.1 - fix(classes)
cleanup resources, add cancellable timeouts, and fix bugs in several core utility classes
- Replace one-shot delayFor usage with plugins.smartdelay.Timeout in AsyncExecutionStack so timeouts are cancellable and properly cleaned up on success or error
- Add destroy() to BackpressuredArray to complete subjects and unblock waiters; waitForSpace/waitForItems now respect destruction to avoid hangs
- Make Interest instances cancel mark-lost timers and guard against double-destroy; destruction now clears fulfillment store and resolves default fulfillment without mutual recursion
- Add InterestMap.destroy() to clean up all interests and complete observable
- ObjectMap: removeMappedUnique now returns removed object and emits a remove event; wipe now emits remove events for cleared entries and destroy() completes eventSubject
- StringMap.destroy() clears stored strings and pending triggers
- TimedAggregtor: add stop(flushRemaining) and isStopped guards to stop timer chain and optionally flush remaining items
- LoopTracker: add reset() and destroy() helpers to clear and destroy internal maps
- Fix compareTreePosition to call symbolTree.compareTreePosition instead of recursively calling itself
## 2026-03-01 - 6.3.0 - feat(tooling)
update build tooling, developer dependencies, npmextra configuration, and expand README documentation
- Bump devDependencies for @git.zone toolchain and related packages (@git.zone/tsbuild, tsbundle, tsrun, tstest, @push.rocks/tapbundle, @types/node)
- Bump runtime deps: @push.rocks/smartrx and @push.rocks/smarttime
- Adjust npm build script: remove trailing 'npm' argument from tsbundle invocation
- Rework npmextra.json: rename/unify keys to @git.zone/* scoped entries, add release registries and accessLevel, add tsbundle bundle configuration, and reorganize CI/tool settings
- Significant README rewrite: expanded descriptions, clearer usage examples and API snippets, formatting and example updates
## 2026-03-01 - 6.2.3 - fix(interestmap)
remove interest from InterestMap immediately after fulfillment
- Call destroy() in fullfillInterest to remove the interest entry from the InterestMap right after resolving interestDeferred.
- Prevents stale entries and ensures immediate cleanup of fulfilled interests
## 2025-04-25 - 6.2.2 - fix(docs)
Update @push.rocks/tapbundle dependency and refine AsyncExecutionStack documentation examples
- Bump @push.rocks/tapbundle from ^5.0.8 to ^5.5.6 in package.json
- Improve README documentation for AsyncExecutionStack with clearer examples for exclusive and non-exclusive task execution
- Demonstrate usage of concurrency controls in AsyncExecutionStack
## 2025-04-25 - 6.2.1 - fix(AsyncExecutionStack tests)
Refactor AsyncExecutionStack tests: update non-exclusive concurrency assertions and clean up test logic
- Replace 'toBe' with 'toEqual' for active and pending counts to ensure consistency
- Simplify default non-exclusive concurrency test by asserting Infinity is non-finite using toBeFalse
- Adjust test comments and scheduling for clarity in concurrency behavior
## 2025-04-25 - 6.2.0 - feat(AsyncExecutionStack)
Improve non-exclusive task management with concurrency limit controls and enhanced monitoring in AsyncExecutionStack.
- Added methods to set and get non-exclusive concurrency limits and statistics (setNonExclusiveMaxConcurrency, getActiveNonExclusiveCount, getPendingNonExclusiveCount, and getNonExclusiveMaxConcurrency).
- Integrated proper waiting and release mechanisms for non-exclusive slots.
- Extended test coverage to validate concurrency limits and ensure correct behavior.
## 2024-10-13 - 6.1.0 - feat(BackpressuredArray)
Add method to check if items are present in BackpressuredArray
- Implemented a new method `checkHasItems` in the BackpressuredArray class to determine if the array contains any items.
## 2024-05-29 to 2024-04-18 - 6.0.15
Minor updates were made to documentation and descriptions.
- Update project description
## 2024-04-18 to 2024-02-25 - 6.0.14
Several updates were made to configurations and json files.
- Updated core components in the codebase
- Modified tsconfig settings
- Revised npmextra.json with githost configurations
## 2024-02-25 to 2024-02-23 - 6.0.13
No relevant changes.
## 2024-02-23 to 2023-11-13 - 6.0.12 to 6.0.8
Multiple core updates were performed to ensure stability and performance.
- Fixed various issues in core components
## 2023-11-13 to 2023-08-14 - 6.0.7 to 6.0.3
Minor internal core updates.
## 2023-08-14 to 2023-07-12 - 6.0.2
Implemented a switch to a new organizational scheme.
## 2023-01-18 to 2022-05-27 - 6.0.0
Updated core functionalities; introduced breaking changes for compatibility with ECMAScript modules.
- Core updates
- Switching from CommonJS to ECMAScript modules
## 2022-05-27 to 2022-05-27 - 5.0.6 to 5.0.0
Minor updates and a significant change in `objectmap` behavior to support async operations.
- Included async behaviors in objectmap as a breaking change
## 2020-05-04 to 2020-02-17 - 4.0.0
Refactored ObjectMap; introduced new features.
- Refactored ObjectMap with concat functionality as a breaking change
- Added .clean() to FastMap
## 2020-02-17 to 2020-02-06 - 3.0.19 to 3.0.15
Enhancements and new functionality in ObjectMap.
- Added object mapping enhancements
- Introduced object map with unique keys

View File

@@ -1,17 +1,49 @@
{ {
"npmci": { "@git.zone/cli": {
"npmGlobalTools": [],
"npmAccessLevel": "public"
},
"gitzone": {
"projectType": "npm", "projectType": "npm",
"module": { "module": {
"githost": "gitlab.com", "githost": "code.foss.global",
"gitscope": "pushrocks", "gitscope": "push.rocks",
"gitrepo": "lik", "gitrepo": "lik",
"shortDescription": "light little helpers for node", "description": "Provides a collection of lightweight helpers and utilities for Node.js projects.",
"npmPackagename": "@pushrocks/lik", "npmPackagename": "@push.rocks/lik",
"license": "MIT" "license": "MIT",
} "keywords": [
"Utilities",
"Helpers",
"Typescript",
"Node.js",
"String manipulation",
"Object management",
"Execution control",
"Data structures",
"Asynchronous programming",
"Event handling",
"Data aggregation"
]
},
"release": {
"registries": [
"https://verdaccio.lossless.digital",
"https://registry.npmjs.org"
],
"accessLevel": "public"
}
},
"@git.zone/tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
},
"@git.zone/tsbundle": {
"bundles": [
{
"from": "./ts/index.ts",
"to": "./dist_bundle/bundle.js",
"outputMode": "bundle",
"bundler": "esbuild"
}
]
},
"@ship.zone/szci": {
"npmGlobalTools": []
} }
} }

27859
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,41 +1,42 @@
{ {
"name": "@pushrocks/lik", "name": "@push.rocks/lik",
"version": "5.0.2", "version": "6.3.1",
"private": false, "private": false,
"description": "light little helpers for node", "description": "Provides a collection of lightweight helpers and utilities for Node.js projects.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts", "typings": "dist_ts/index.d.ts",
"type": "module",
"scripts": { "scripts": {
"test": "(tstest test/)", "test": "(tstest test/)",
"build": "(tsbuild --web && tsbundle npm)" "build": "(tsbuild --web --allowimplicitany && tsbundle)",
"buildDocs": "tsdoc"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+ssh://git@gitlab.com/pushrocks/lik.git" "url": "https://code.foss.global/push.rocks/lik.git"
}, },
"author": "Lossless GmbH", "author": "Lossless GmbH",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://gitlab.com/pushrocks/lik/issues" "url": "https://gitlab.com/pushrocks/lik/issues"
}, },
"homepage": "https://gitlab.com/pushrocks/lik#README", "homepage": "https://code.foss.global/push.rocks/lik",
"devDependencies": { "devDependencies": {
"@gitzone/tsbuild": "^2.1.29", "@git.zone/tsbuild": "^4.1.2",
"@gitzone/tsbundle": "^1.0.89", "@git.zone/tsbundle": "^2.9.0",
"@gitzone/tsrun": "^1.2.18", "@git.zone/tsrun": "^2.0.1",
"@gitzone/tstest": "^1.0.60", "@git.zone/tstest": "^3.1.8",
"@pushrocks/tapbundle": "^4.0.0", "@push.rocks/tapbundle": "^6.0.3",
"@types/node": "^17.0.10", "@types/node": "^25.3.3"
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0"
}, },
"dependencies": { "dependencies": {
"@pushrocks/smartdelay": "^2.0.13", "@push.rocks/smartdelay": "^3.0.5",
"@pushrocks/smartmatch": "^1.0.7", "@push.rocks/smartmatch": "^2.0.0",
"@pushrocks/smartpromise": "^3.1.6", "@push.rocks/smartpromise": "^4.0.3",
"@pushrocks/smartrx": "^2.0.22", "@push.rocks/smartrx": "^3.0.10",
"@pushrocks/smarttime": "^3.0.43", "@push.rocks/smarttime": "^4.2.3",
"@types/minimatch": "^3.0.5", "@types/minimatch": "^5.1.2",
"@types/symbol-tree": "^3.2.5",
"symbol-tree": "^3.2.4" "symbol-tree": "^3.2.4"
}, },
"files": [ "files": [
@@ -52,5 +53,26 @@
], ],
"browserslist": [ "browserslist": [
"last 1 chrome versions" "last 1 chrome versions"
],
"keywords": [
"Utilities",
"Helpers",
"Typescript",
"Node.js",
"String manipulation",
"Object management",
"Execution control",
"Data structures",
"Asynchronous programming",
"Event handling",
"Data aggregation"
],
"pnpm": {
"onlyBuiltDependencies": [
"esbuild",
"mongodb-memory-server",
"puppeteer"
] ]
},
"packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
} }

12100
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

1
readme.hints.md Normal file
View File

@@ -0,0 +1 @@

335
readme.md
View File

@@ -1,54 +1,315 @@
# @pushrocks/lik # @push.rocks/lik
light little helpers for node
## Availabililty and Links A collection of lightweight utility classes for TypeScript/Node.js projects, providing efficient data structures and async helpers.
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/lik)
* [gitlab.com (source)](https://gitlab.com/pushrocks/lik)
* [github.com (source mirror)](https://github.com/pushrocks/lik)
* [docs (typedoc)](https://pushrocks.gitlab.io/lik/)
## Status for master ## Install
Status Category | Status Badge ```bash
-- | -- npm install @push.rocks/lik
GitLab Pipelines | [![pipeline status](https://gitlab.com/pushrocks/lik/badges/master/pipeline.svg)](https://lossless.cloud) ```
GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/pushrocks/lik/badges/master/coverage.svg)](https://lossless.cloud)
npm | [![npm downloads per month](https://badgen.net/npm/dy/@pushrocks/lik)](https://lossless.cloud)
Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/pushrocks/lik)](https://lossless.cloud)
TypeScript Support | [![TypeScript](https://badgen.net/badge/TypeScript/>=%203.x/blue?icon=typescript)](https://lossless.cloud)
node Support | [![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
Code Style | [![Code Style](https://badgen.net/badge/style/prettier/purple)](https://lossless.cloud)
PackagePhobia (total standalone install weight) | [![PackagePhobia](https://badgen.net/packagephobia/install/@pushrocks/lik)](https://lossless.cloud)
PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@pushrocks/lik)](https://lossless.cloud)
BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@pushrocks/lik)](https://lossless.cloud)
Platform support | [![Supports Windows 10](https://badgen.net/badge/supports%20Windows%2010/yes/green?icon=windows)](https://lossless.cloud) [![Supports Mac OS X](https://badgen.net/badge/supports%20Mac%20OS%20X/yes/green?icon=apple)](https://lossless.cloud)
## Usage ## Usage
Use TypeScript for best in class instellisense. `@push.rocks/lik` provides a set of focused helper classes for common programming tasks: managing collections, controlling async execution, tracking interests, and more. All classes are fully typed and work in both Node.js and browser environments.
```javascript ### AsyncExecutionStack
// import any tool that you need from lik
import { Stringmap, Objectmap, Observablemap } from 'lik'; Controls execution of asynchronous tasks in two modes:
- **Exclusive**: tasks run one at a time, blocking all others until complete.
- **Non-exclusive**: tasks run in parallel with optional concurrency limits.
```typescript
import { AsyncExecutionStack } from '@push.rocks/lik';
const stack = new AsyncExecutionStack();
// Exclusive execution (sequential, blocks other tasks)
await stack.getExclusiveExecutionSlot(async () => {
// critical section work
}, 5000); // optional timeout in ms
// Non-exclusive execution (parallel)
const p1 = stack.getNonExclusiveExecutionSlot(async () => {
// concurrent work 1
});
const p2 = stack.getNonExclusiveExecutionSlot(async () => {
// concurrent work 2
});
await Promise.all([p1, p2]);
// Control concurrency
stack.setNonExclusiveMaxConcurrency(3);
console.log(stack.getNonExclusiveMaxConcurrency()); // 3
console.log(stack.getActiveNonExclusiveCount()); // currently running
console.log(stack.getPendingNonExclusiveCount()); // waiting for slots
``` ```
### class Stringmap ### BackpressuredArray
Stringmap allows you to keep track of strings. It allows you to register triggers for certain events An array with backpressure support using RxJS subjects. Useful for producer/consumer patterns where you need to throttle the producer when the consumer can't keep up.
like when a certain string is removed or added to the map
### class Objectmap ```typescript
import { BackpressuredArray } from '@push.rocks/lik';
Sometimes you need to keep track of objects, but implementing logic for removing, finding or updating is tedious. const buffer = new BackpressuredArray<string>(16); // high water mark of 16
Objectmap takes care of keeping track of objects for you.
## Contribution // Producer: push items, returns false when full
const hasSpace = buffer.push('item1');
if (!hasSpace) {
await buffer.waitForSpace(); // wait until consumer frees space
}
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :) // Consumer: shift items out
await buffer.waitForItems(); // wait until items are available
const item = buffer.shift();
For further information read the linked docs at the top of this readme. // Check state
buffer.checkSpaceAvailable(); // true if below high water mark
buffer.checkHasItems(); // true if items exist
```
> MIT licensed | **&copy;** [Lossless GmbH](https://lossless.gmbh) ### FastMap
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
[![repo-footer](https://lossless.gitlab.io/publicrelations/repofooter.svg)](https://maintainedby.lossless.com) A high-performance key-value map optimized for rapid lookups and modifications.
```typescript
import { FastMap } from '@push.rocks/lik';
const map = new FastMap<string>();
map.addToMap('key1', 'value1');
map.addToMap('key2', 'value2');
const value = map.getByKey('key1'); // 'value1'
map.isUniqueKey('key1'); // false (already exists)
map.removeFromMap('key1');
// Merge maps
const otherMap = new FastMap<string>();
otherMap.addToMap('key3', 'value3');
const merged = map.concat(otherMap);
// Or merge in place
map.addAllFromOther(otherMap);
// Async find
const found = await map.find(async (item) => item === 'value2');
```
### InterestMap
Manages subscriptions/interests in events or entities. Multiple parties can express interest in the same thing; the interest is deduplicated and fulfilled once.
```typescript
import { InterestMap } from '@push.rocks/lik';
const interestMap = new InterestMap<string, number>(
(str) => str, // comparison function to deduplicate interests
{ markLostAfterDefault: 30000 } // optional: auto-mark lost after 30s
);
// Express interest
const interest = await interestMap.addInterest('event1');
// Wait for fulfillment
interest.interestFullfilled.then((result) => {
console.log('Got result:', result);
});
// Fulfill from elsewhere
const found = interestMap.findInterest('event1');
found.fullfillInterest(42);
// Check and manage interests
interestMap.checkInterest('event1'); // true/false
interestMap.informLostInterest('event1'); // starts destruction timer
// Observable stream of new interests
interestMap.interestObservable; // ObservableIntake<Interest>
```
### LimitedArray
An array that automatically enforces a maximum size, discarding oldest items when the limit is exceeded.
```typescript
import { LimitedArray } from '@push.rocks/lik';
const arr = new LimitedArray<number>(5);
arr.addMany([1, 2, 3, 4, 5, 6]);
console.log(arr.array.length); // 5 (oldest items dropped)
arr.addOne(7);
arr.setLimit(3); // dynamically adjust limit
// Compute average (for numeric arrays)
const numArr = new LimitedArray<number>(10);
numArr.addMany([10, 20, 30]);
console.log(numArr.getAverage()); // 20
```
### LoopTracker
Detects and prevents infinite loops by tracking object references during iterations.
```typescript
import { LoopTracker } from '@push.rocks/lik';
const tracker = new LoopTracker<object>();
const obj1 = {};
tracker.checkAndTrack(obj1); // true (first time, tracked)
tracker.checkAndTrack(obj1); // false (already seen - loop detected!)
```
### ObjectMap
A managed collection of objects with add/remove/find operations and event notifications via RxJS.
```typescript
import { ObjectMap } from '@push.rocks/lik';
interface IUser {
id: number;
name: string;
}
const users = new ObjectMap<IUser>();
// Add objects
const key = users.add({ id: 1, name: 'Alice' });
users.addArray([{ id: 2, name: 'Bob' }, { id: 3, name: 'Carol' }]);
// Find objects
const alice = users.findSync((u) => u.id === 1);
const bob = await users.find(async (u) => u.id === 2);
// Find and remove in one step
const removed = await users.findOneAndRemove(async (u) => u.id === 3);
const removedSync = users.findOneAndRemoveSync((u) => u.id === 2);
// Direct add/get by unique key
users.addMappedUnique('admin', { id: 99, name: 'Admin' });
const admin = users.getMappedUnique('admin');
// Get one and remove (FIFO-style)
const first = users.getOneAndRemove();
// Iterate, check, and manage
await users.forEach((u) => console.log(u.name));
users.checkForObject(alice); // true/false
users.isEmpty(); // true/false
users.getArray(); // cloned array of all objects
users.wipe(); // remove all
// Listen for changes
users.eventSubject.subscribe((event) => {
console.log(event.operation, event.payload); // 'add' | 'remove'
});
// Merge object maps
const merged = users.concat(otherObjectMap);
users.addAllFromOther(otherObjectMap);
```
### Stringmap
Manages a collection of strings with add/remove/query operations and minimatch pattern matching.
```typescript
import { Stringmap } from '@push.rocks/lik';
const strings = new Stringmap();
strings.addString('hello');
strings.addStringArray(['world', 'example']);
strings.checkString('hello'); // true
strings.checkMinimatch('hel*'); // true (glob matching)
strings.checkIsEmpty(); // false
strings.removeString('hello');
strings.getStringArray(); // ['world', 'example']
// Register trigger that fires when condition is met
await strings.registerUntilTrue((arr) => arr.length === 0);
strings.wipe(); // triggers the above
```
### TimedAggregator
Batches items over a time interval, then processes them in bulk. Useful for aggregating logs, metrics, or events.
```typescript
import { TimedAggregtor } from '@push.rocks/lik';
const aggregator = new TimedAggregtor<string>({
aggregationIntervalInMillis: 5000,
functionForAggregation: (items) => {
console.log('Batch:', items);
},
});
aggregator.add('event1');
aggregator.add('event2');
// After 5 seconds: Batch: ['event1', 'event2']
```
### Tree
A typed wrapper around `symbol-tree` for managing hierarchical data structures with parent/child/sibling relationships.
```typescript
import { Tree } from '@push.rocks/lik';
class TreeNode {
constructor(public value: string) {}
}
const tree = new Tree<TreeNode>();
const root = new TreeNode('root');
tree.initialize(root);
const child1 = new TreeNode('child1');
const child2 = new TreeNode('child2');
tree.appendChild(root, child1);
tree.appendChild(root, child2);
// Navigate
tree.hasChildren(root); // true
tree.firstChild(root); // child1
tree.lastChild(root); // child2
tree.nextSibling(child1); // child2
tree.parent(child1); // root
// Query
tree.childrenCount(root); // 2
tree.index(child2); // 1
tree.childrenToArray(root, {}); // [child1, child2]
tree.treeToArray(root, {}); // full tree as array
// Mutate
tree.insertBefore(child2, new TreeNode('between'));
tree.remove(child2);
```
## License and Legal Information
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
### Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
### Company Information
Task Venture Capital GmbH
Registered at District court Bremen HRB 35230 HB, Germany
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

View File

@@ -0,0 +1,91 @@
import { tap, expect } from '@push.rocks/tapbundle';
import * as lik from '../ts/index.js';
let testAsyncExecutionStack: lik.AsyncExecutionStack;
tap.test('should create a valid instance of AsyncExectionStack', async () => {
testAsyncExecutionStack = new lik.AsyncExecutionStack();
expect(testAsyncExecutionStack).toBeInstanceOf(lik.AsyncExecutionStack);
});
tap.test('should run in parallel', async (toolsArg) => {
await testAsyncExecutionStack.getExclusiveExecutionSlot(async () => {
await toolsArg.delayFor(2000);
console.log('should run first');
}, 2500);
testAsyncExecutionStack.getNonExclusiveExecutionSlot(async () => {
await toolsArg.delayFor(2000);
console.log('should run third');
}, 2500);
testAsyncExecutionStack.getNonExclusiveExecutionSlot(async () => {
await toolsArg.delayFor(1000);
console.log('should run second');
}, 2500);
await testAsyncExecutionStack.getExclusiveExecutionSlot(async () => {
console.log('should run fourth');
}, 0);
});
// Test default non-exclusive has no concurrency limit property (Infinity)
tap.test('default non-exclusive has no concurrency limit', () => {
const stack = new lik.AsyncExecutionStack();
// default maxConcurrency is Infinity (not finite)
expect(Number.isFinite(stack.getNonExclusiveMaxConcurrency())).toBeFalse();
});
// Test respecting a non-exclusive concurrency limit
tap.test('non-exclusive respects maxConcurrency', async (tools) => {
const stack = new lik.AsyncExecutionStack();
stack.setNonExclusiveMaxConcurrency(2);
const activeCounts: number[] = [];
const tasks: Promise<void>[] = [];
for (let i = 0; i < 5; i++) {
tasks.push(
stack.getNonExclusiveExecutionSlot(async () => {
activeCounts.push(stack.getActiveNonExclusiveCount());
await tools.delayFor(50);
})
);
}
await Promise.all(tasks);
// never more than 2 at once
const maxActive = Math.max(...activeCounts);
expect(maxActive).toBeLessThanOrEqual(2);
});
// Test concurrency stats (active vs pending) for non-exclusive tasks
tap.test('non-exclusive concurrency stats reflect active and pending', async (tools) => {
const stack = new lik.AsyncExecutionStack();
stack.setNonExclusiveMaxConcurrency(2);
// initially, no tasks
expect(stack.getActiveNonExclusiveCount()).toEqual(0);
expect(stack.getPendingNonExclusiveCount()).toEqual(0);
// enqueue four tasks
const p1 = stack.getNonExclusiveExecutionSlot(async () => {
await tools.delayFor(30);
});
const p2 = stack.getNonExclusiveExecutionSlot(async () => {
await tools.delayFor(30);
});
const p3 = stack.getNonExclusiveExecutionSlot(async () => {
await tools.delayFor(30);
});
const p4 = stack.getNonExclusiveExecutionSlot(async () => {
await tools.delayFor(30);
});
// wait for first task to finish and scheduling of next batch
await p1;
await tools.delayFor(0);
// second batch: two active, one pending (4 tasks, limit=2)
expect(stack.getActiveNonExclusiveCount()).toEqual(2);
expect(stack.getPendingNonExclusiveCount()).toEqual(1);
// wait for remaining tasks to complete
await Promise.all([p2, p3, p4]);
// after completion, counts reset
expect(stack.getActiveNonExclusiveCount()).toEqual(0);
expect(stack.getPendingNonExclusiveCount()).toEqual(0);
});
export default tap.start();

View File

@@ -1,6 +1,6 @@
import { tap, expect } from '@pushrocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import * as lik from '../ts'; import * as lik from '../ts/index.js';
tap.test('should create a valid fastmap', async () => { tap.test('should create a valid fastmap', async () => {
const fastmap = new lik.FastMap(); const fastmap = new lik.FastMap();
@@ -14,15 +14,17 @@ tap.test('should find an entry', async () => {
}>(); }>();
fastmap.addToMap('heythere', { fastmap.addToMap('heythere', {
value1: 'heyho', value1: 'heyho',
value2: 'heyho2' value2: 'heyho2',
}) });
fastmap.addToMap('heythere2', { fastmap.addToMap('heythere2', {
value1: 'heyho3', value1: 'heyho3',
value2: 'heyho4' value2: 'heyho4',
}); });
const result = await fastmap.find(async (itemArg)=> {return itemArg.value2 === 'heyho4'}); const result = await fastmap.find(async (itemArg) => {
return itemArg.value2 === 'heyho4';
});
expect(result.value1).toEqual('heyho3'); expect(result.value1).toEqual('heyho3');
}); });
tap.start(); export default tap.start();

View File

@@ -1,5 +1,5 @@
import { tap, expect } from '@pushrocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import * as lik from '../ts/index'; import * as lik from '../ts/index.js';
let testInterestmap: lik.InterestMap<number, number>; let testInterestmap: lik.InterestMap<number, number>;
@@ -22,4 +22,4 @@ tap.test('should be able to inform about a lost interest', async () => {
testInterestmap.informLostInterest(3); testInterestmap.informLostInterest(3);
}); });
tap.start(); export default tap.start();

View File

@@ -1,6 +1,6 @@
import { tap, expect } from '@pushrocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import { LimitedArray } from '../ts/index'; import { LimitedArray } from '../ts/index.js';
let testLimitedArray: LimitedArray<string>; let testLimitedArray: LimitedArray<string>;
@@ -14,4 +14,4 @@ tap.test('should never be longer than the set length', async () => {
expect(testLimitedArray.array.length < 7).toBeTrue(); expect(testLimitedArray.array.length < 7).toBeTrue();
}); });
tap.start(); export default tap.start();

View File

@@ -1,10 +1,8 @@
// import test framework // import test framework
import { expect, tap } from '@pushrocks/tapbundle'; import { expect, tap } from '@push.rocks/tapbundle';
import * as events from 'events';
import * as smartpromise from '@pushrocks/smartpromise';
// import the module // import the module
import * as lik from '../ts/index'; import * as lik from '../ts/index.js';
let object1 = {}; let object1 = {};
let object2 = {}; let object2 = {};
@@ -23,4 +21,4 @@ tap.test('should add objects once and return true', async () => {
expect(myLoopTracker.checkAndTrack(object2)).toBeFalse(); expect(myLoopTracker.checkAndTrack(object2)).toBeFalse();
}); });
tap.start(); export default tap.start();

View File

@@ -1,10 +1,8 @@
// import test framework // import test framework
import { expect, tap } from '@pushrocks/tapbundle'; import { expect, tap } from '@push.rocks/tapbundle';
import * as events from 'events';
import * as smartpromise from '@pushrocks/smartpromise';
// import the module // import the module
import * as lik from '../ts/index'; import * as lik from '../ts/index.js';
// Objectmap // Objectmap
interface ITestObject { interface ITestObject {
@@ -45,7 +43,7 @@ tap.test('lik.Objectmap.remove() -> should correctly remove an object to Objectm
tap.test('Objectmap.forEach -> should correctly run a function forEach map object', async () => { tap.test('Objectmap.forEach -> should correctly run a function forEach map object', async () => {
testObjectmap.forEach((itemArg) => { testObjectmap.forEach((itemArg) => {
// TODO expect(itemArg).to.have.property('propOne'); expect(itemArg).toHaveProperty('propOne');
}); });
}); });
@@ -70,9 +68,9 @@ tap.test('should get one object and then remove it', async () => {
let originalLength = testObjectmap.getArray().length; let originalLength = testObjectmap.getArray().length;
let oneObject = testObjectmap.getOneAndRemove(); let oneObject = testObjectmap.getOneAndRemove();
// tslint:disable-next-line:no-unused-expression // tslint:disable-next-line:no-unused-expression
// TODO expect(oneObject).not.toBeNull(); expect(oneObject).not.toBeNull();
expect(testObjectmap.getArray().length).toEqual(originalLength - 1); expect(testObjectmap.getArray().length).toEqual(originalLength - 1);
// TODO expect(testObjectmap.getArray()).to.not.contain(oneObject); expect(testObjectmap.getArray()).not.toContain(oneObject);
}); });
tap.start(); export default tap.start();

View File

@@ -1,10 +1,8 @@
// import test framework // import test framework
import { expect, tap } from '@pushrocks/tapbundle'; import { expect, tap } from '@push.rocks/tapbundle';
import * as events from 'events';
import * as smartpromise from '@pushrocks/smartpromise';
// import the module // import the module
import * as lik from '../ts/index'; import * as lik from '../ts/index.js';
// testData // testData
let testStringmap: lik.Stringmap; let testStringmap: lik.Stringmap;
@@ -85,4 +83,4 @@ tap.test('lik.Stringmap.empty() -> should remove wipe and then notify', async ()
testStringmap.wipe(); testStringmap.wipe();
}); });
tap.start(); export default tap.start();

View File

@@ -1,10 +1,8 @@
// import test framework // import test framework
import { expect, tap } from '@pushrocks/tapbundle'; import { expect, tap } from '@push.rocks/tapbundle';
import * as events from 'events';
import * as smartpromise from '@pushrocks/smartpromise';
// import the module // import the module
import * as lik from '../ts/index'; import * as lik from '../ts/index.js';
let testTimedAggregator: lik.TimedAggregtor<string>; let testTimedAggregator: lik.TimedAggregtor<string>;
@@ -22,4 +20,4 @@ tap.test('should create a timed aggregaotor', async (tools) => {
await tools.delayFor(2000); await tools.delayFor(2000);
}); });
tap.start(); export default tap.start();

View File

@@ -1,5 +1,5 @@
import { tap, expect } from '@pushrocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import * as lik from '../ts/index'; import * as lik from '../ts/index.js';
class TestClass { class TestClass {
constructor(public hey: string) { constructor(public hey: string) {
@@ -24,7 +24,7 @@ tap.test('create a valid tree instance', async () => {
tap.test('should insert an object', async () => { tap.test('should insert an object', async () => {
testTree.initialize(testInstance); testTree.initialize(testInstance);
let resultArray = testTree.treeToArray(testInstance, {}); let resultArray = testTree.treeToArray(testInstance, {});
// TODO expect(resultArray).toContain(testInstance); expect(resultArray).toContain(testInstance);
}); });
tap.test('should add other objects in a hierachy', async () => { tap.test('should add other objects in a hierachy', async () => {
@@ -37,4 +37,4 @@ tap.test("should create a JSON object that reflects a tree's hierachy", async ()
const jsonTreet = testTree.toJsonWithHierachy(testInstance); const jsonTreet = testTree.toJsonWithHierachy(testInstance);
}); });
tap.start(); export default tap.start();

8
ts/00_commitinfo_data.ts Normal file
View File

@@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @push.rocks/commitinfo
*/
export const commitinfo = {
name: '@push.rocks/lik',
version: '6.3.1',
description: 'Provides a collection of lightweight helpers and utilities for Node.js projects.'
}

View File

@@ -0,0 +1,177 @@
import * as plugins from './classes.plugins.js';
interface IExecutionSlot<T> {
executionDeferred: plugins.smartpromise.Deferred<T>;
funcToExecute: () => Promise<T>;
timeout?: number;
mode: 'exclusive' | 'nonexclusive';
}
export class AsyncExecutionStack {
private executionSlots: IExecutionSlot<any>[] = [];
private isProcessing = false;
/** Maximum concurrent non-exclusive tasks (Infinity = unlimited) */
private nonExclusiveMaxConcurrency: number = Infinity;
/** Currently running non-exclusive task count */
private nonExclusiveCurrentCount: number = 0;
/** Queue of resolvers waiting for a non-exclusive slot */
private nonExclusivePendingQueue: Array<() => void> = [];
public async getExclusiveExecutionSlot<T = any>(
funcArg: () => Promise<T>,
timeoutArg?: number
): Promise<T> {
const executionDeferred = plugins.smartpromise.defer<T>();
const executionSlot: IExecutionSlot<T> = {
funcToExecute: funcArg,
executionDeferred,
timeout: timeoutArg,
mode: 'exclusive',
};
this.executionSlots.push(executionSlot);
this.processExecutionSlots();
return executionDeferred.promise;
}
public async getNonExclusiveExecutionSlot<T = any>(
funcArg: () => Promise<T>,
timeoutArg?: number
): Promise<T> {
const executionDeferred = plugins.smartpromise.defer<T>();
const executionSlot: IExecutionSlot<T> = {
funcToExecute: funcArg,
executionDeferred,
timeout: timeoutArg,
mode: 'nonexclusive',
};
this.executionSlots.push(executionSlot);
this.processExecutionSlots();
return executionDeferred.promise;
}
/**
* Set the maximum number of concurrent non-exclusive tasks.
* @param concurrency minimum 1 (Infinity means unlimited)
*/
public setNonExclusiveMaxConcurrency(concurrency: number): void {
if (!Number.isFinite(concurrency) || concurrency < 1) {
throw new Error('nonExclusiveMaxConcurrency must be a finite number >= 1');
}
this.nonExclusiveMaxConcurrency = concurrency;
}
/** Get the configured max concurrency for non-exclusive tasks */
public getNonExclusiveMaxConcurrency(): number {
return this.nonExclusiveMaxConcurrency;
}
/** Number of non-exclusive tasks currently running */
public getActiveNonExclusiveCount(): number {
return this.nonExclusiveCurrentCount;
}
/** Number of non-exclusive tasks waiting for a free slot */
public getPendingNonExclusiveCount(): number {
return this.nonExclusivePendingQueue.length;
}
private async processExecutionSlots() {
if (this.isProcessing) {
return;
}
this.isProcessing = true;
while (this.executionSlots.length > 0) {
const currentSlot = this.executionSlots[0];
if (currentSlot.mode === 'exclusive') {
await this.executeExclusiveSlot(currentSlot);
this.executionSlots.shift();
} else {
// Gather all non-exclusive slots at the front of the queue
const nonExclusiveSlots: IExecutionSlot<any>[] = [];
while (this.executionSlots.length > 0 && this.executionSlots[0].mode === 'nonexclusive') {
nonExclusiveSlots.push(this.executionSlots.shift()!);
}
await this.executeNonExclusiveSlots(nonExclusiveSlots);
}
}
this.isProcessing = false;
}
private async executeExclusiveSlot(slot: IExecutionSlot<any>) {
try {
if (slot.timeout) {
const timeoutInstance = new plugins.smartdelay.Timeout(slot.timeout);
try {
const result = await Promise.race([
slot.funcToExecute(),
timeoutInstance.promise.then(() => {
throw new Error('Timeout reached');
}),
]);
timeoutInstance.cancel();
slot.executionDeferred.resolve(result);
} catch (error) {
timeoutInstance.cancel();
throw error;
}
} else {
const result = await slot.funcToExecute();
slot.executionDeferred.resolve(result);
}
} catch (error) {
slot.executionDeferred.reject(error);
}
}
private async executeNonExclusiveSlots(slots: IExecutionSlot<any>[]) {
const promises = slots.map(async (slot) => {
// wait for an available non-exclusive slot
await this.waitForNonExclusiveSlot();
try {
// execute with optional timeout
if (slot.timeout) {
const timeoutInstance = new plugins.smartdelay.Timeout(slot.timeout);
try {
const result = await Promise.race([
slot.funcToExecute(),
timeoutInstance.promise.then(() => { throw new Error('Timeout reached'); }),
]);
timeoutInstance.cancel();
slot.executionDeferred.resolve(result);
} catch (error) {
timeoutInstance.cancel();
throw error;
}
} else {
const result = await slot.funcToExecute();
slot.executionDeferred.resolve(result);
}
} catch (error) {
slot.executionDeferred.reject(error);
} finally {
this.releaseNonExclusiveSlot();
}
});
await Promise.all(promises);
}
/**
* Wait until a non-exclusive slot is available (respects max concurrency).
*/
private waitForNonExclusiveSlot(): Promise<void> {
if (this.nonExclusiveCurrentCount < this.nonExclusiveMaxConcurrency) {
this.nonExclusiveCurrentCount++;
return Promise.resolve();
}
return new Promise((resolve) => {
this.nonExclusivePendingQueue.push(() => {
this.nonExclusiveCurrentCount++;
resolve();
});
});
}
/** Release a non-exclusive slot and wake the next waiter, if any. */
private releaseNonExclusiveSlot(): void {
this.nonExclusiveCurrentCount--;
const next = this.nonExclusivePendingQueue.shift();
if (next) {
next();
}
}
}

View File

@@ -0,0 +1,86 @@
import * as plugins from './classes.plugins.js';
export class BackpressuredArray<T> {
public data: T[];
private highWaterMark: number;
public hasSpace = new plugins.smartrx.rxjs.Subject<'hasSpace'>();
private itemsAvailable = new plugins.smartrx.rxjs.Subject<'itemsAvailable'>();
private isDestroyed = false;
constructor(highWaterMark: number = 16) {
this.data = [];
this.highWaterMark = highWaterMark;
}
push(item: T): boolean {
this.data.push(item);
this.itemsAvailable.next('itemsAvailable');
const spaceAvailable = this.checkSpaceAvailable();
if (spaceAvailable) {
this.hasSpace.next('hasSpace');
}
return spaceAvailable;
}
shift(): T | undefined {
const item = this.data.shift();
if (this.checkSpaceAvailable()) {
this.hasSpace.next('hasSpace');
}
return item;
}
checkSpaceAvailable(): boolean {
return this.data.length < this.highWaterMark;
}
public checkHasItems(): boolean {
return this.data.length > 0;
}
waitForSpace(): Promise<void> {
return new Promise<void>((resolve) => {
if (this.checkSpaceAvailable() || this.isDestroyed) {
resolve();
} else {
const subscription = this.hasSpace.subscribe({
next: () => {
subscription.unsubscribe();
resolve();
},
complete: () => {
resolve();
},
});
}
});
}
waitForItems(): Promise<void> {
return new Promise<void>((resolve) => {
if (this.data.length > 0 || this.isDestroyed) {
resolve();
} else {
const subscription = this.itemsAvailable.subscribe({
next: () => {
subscription.unsubscribe();
resolve();
},
complete: () => {
resolve();
},
});
}
});
}
/**
* destroys the BackpressuredArray, completing all subjects
*/
public destroy() {
this.isDestroyed = true;
this.hasSpace.complete();
this.itemsAvailable.complete();
}
}

View File

@@ -1,4 +1,9 @@
import * as plugins from './lik.plugins'; /* ============
The FastMap has the goal of creating the fastes to use map possible in JS
============ */
import * as plugins from './classes.plugins.js';
/** /**
* fast map allows for very quick lookups of objects with a unique key * fast map allows for very quick lookups of objects with a unique key

View File

@@ -1,6 +1,6 @@
import * as plugins from './lik.plugins'; import * as plugins from './classes.plugins.js';
import { InterestMap, IInterestComparisonFunc } from './lik.interestmap'; import { InterestMap, type IInterestComparisonFunc } from './classes.interestmap.js';
export interface IInterestOptions<DTInterestFullfillment> { export interface IInterestOptions<DTInterestFullfillment> {
markLostAfterDefault: number; markLostAfterDefault: number;
@@ -15,12 +15,18 @@ export class Interest<DTInterestId, DTInterestFullfillment> {
public comparisonFunc: IInterestComparisonFunc<DTInterestId>; public comparisonFunc: IInterestComparisonFunc<DTInterestId>;
public destructionTimer = new plugins.smarttime.Timer(10000); public destructionTimer = new plugins.smarttime.Timer(10000);
public isFullfilled = false; public isFullfilled = false;
private isDestroyed = false;
/** /**
* a generic store to store objects in that are needed for fullfillment; * a generic store to store objects in that are needed for fullfillment;
*/ */
public fullfillmentStore: any[] = []; public fullfillmentStore: any[] = [];
/**
* a cancellable timeout for the markLostAfterDefault feature
*/
private markLostTimeout: InstanceType<typeof plugins.smartdelay.Timeout> | null = null;
/** /**
* quick access to a string that makes the interest comparable for checking for similar interests * quick access to a string that makes the interest comparable for checking for similar interests
*/ */
@@ -28,9 +34,8 @@ export class Interest<DTInterestId, DTInterestFullfillment> {
return this.comparisonFunc(this.originalInterest); return this.comparisonFunc(this.originalInterest);
} }
private interestDeferred: plugins.smartpromise.Deferred< private interestDeferred: plugins.smartpromise.Deferred<DTInterestFullfillment> =
DTInterestFullfillment new plugins.smartpromise.Deferred();
> = new plugins.smartpromise.Deferred();
public interestFullfilled = this.interestDeferred.promise; public interestFullfilled = this.interestDeferred.promise;
/** /**
@@ -40,11 +45,9 @@ export class Interest<DTInterestId, DTInterestFullfillment> {
this.isFullfilled = true; this.isFullfilled = true;
this.fullfillmentStore = []; this.fullfillmentStore = [];
this.interestDeferred.resolve(objectArg); this.interestDeferred.resolve(objectArg);
this.destroy();
} }
/**
*
*/
constructor( constructor(
interestMapArg: InterestMap<DTInterestId, DTInterestFullfillment>, interestMapArg: InterestMap<DTInterestId, DTInterestFullfillment>,
interestArg: DTInterestId, interestArg: DTInterestId,
@@ -57,10 +60,17 @@ export class Interest<DTInterestId, DTInterestFullfillment> {
this.options = optionsArg; this.options = optionsArg;
this.destructionTimer.completed.then(() => { this.destructionTimer.completed.then(() => {
if (!this.isDestroyed) {
this.destroy(); this.destroy();
}
}); });
if (this.options?.markLostAfterDefault) { if (this.options?.markLostAfterDefault) {
plugins.smartdelay.delayFor(this.options.markLostAfterDefault).then(this.markLost); this.markLostTimeout = new plugins.smartdelay.Timeout(this.options.markLostAfterDefault);
this.markLostTimeout.promise.then(() => {
if (!this.isDestroyed) {
this.markLost();
}
});
} }
} }
@@ -72,9 +82,28 @@ export class Interest<DTInterestId, DTInterestFullfillment> {
* self destructs the interest * self destructs the interest
*/ */
public destroy() { public destroy() {
if (this.isDestroyed) {
return;
}
this.isDestroyed = true;
// Cancel timers to release references
this.destructionTimer.reset();
if (this.markLostTimeout) {
this.markLostTimeout.cancel();
this.markLostTimeout = null;
}
// Clear the fulfillment store
this.fullfillmentStore = [];
// Remove from the InterestMap
this.interestMapRef.removeInterest(this); this.interestMapRef.removeInterest(this);
if (!this.isFullfilled && this.options.defaultFullfillment) {
this.fullfillInterest(this.options.defaultFullfillment); // Fulfill with default if not yet fulfilled (inlined to avoid mutual recursion)
if (!this.isFullfilled && this.options?.defaultFullfillment) {
this.isFullfilled = true;
this.interestDeferred.resolve(this.options.defaultFullfillment);
} }
} }

View File

@@ -1,9 +1,16 @@
import * as plugins from './lik.plugins'; /* ===========
import { ObjectMap } from './lik.objectmap'; The InterestMap is an mechanism that collects interests into something
An interest is expressed by an object, string or number.
A comparison func can be specified to make interests comparable
import { Observable } from 'rxjs'; For every unique interestId an interest is created.
Subssequent interests will be mapped to the same interest
which is then is only fullfilled once.
=========== */
import { Interest } from './lik.interestmap.interest'; import * as plugins from './classes.plugins.js';
import { ObjectMap } from './classes.objectmap.js';
import { Interest } from './classes.interestmap.interest.js';
export type IInterestComparisonFunc<T> = (objectArg: T) => string; export type IInterestComparisonFunc<T> = (objectArg: T) => string;
@@ -34,17 +41,17 @@ export class InterestMap<DTInterestId, DTInterestFullfillment> {
/** /**
* adds an interest to the InterestMap * adds an interest to the InterestMap
* @param objectArg * @param interestId
*/ */
public async addInterest( public async addInterest(
objectArg: DTInterestId, interestId: DTInterestId,
defaultFullfillmentArg?: DTInterestFullfillment defaultFullfillmentArg?: DTInterestFullfillment
): Promise<Interest<DTInterestId, DTInterestFullfillment>> { ): Promise<Interest<DTInterestId, DTInterestFullfillment>> {
const comparisonString = this.comparisonFunc(objectArg); const comparisonString = this.comparisonFunc(interestId);
let returnInterest: Interest<DTInterestId, DTInterestFullfillment>; let returnInterest: Interest<DTInterestId, DTInterestFullfillment>;
const newInterest = new Interest<DTInterestId, DTInterestFullfillment>( const newInterest = new Interest<DTInterestId, DTInterestFullfillment>(
this, this,
objectArg, interestId,
this.comparisonFunc, this.comparisonFunc,
{ {
markLostAfterDefault: this.options.markLostAfterDefault, markLostAfterDefault: this.options.markLostAfterDefault,
@@ -63,17 +70,17 @@ export class InterestMap<DTInterestId, DTInterestFullfillment> {
if (!returnInterest) { if (!returnInterest) {
returnInterest = newInterest; returnInterest = newInterest;
this.interestObjectMap.add(returnInterest); this.interestObjectMap.add(returnInterest);
} else {
newInterest.destroy(); // clean up abandoned Interest's timers
} }
this.interestObservable.push(returnInterest); this.interestObservable.push(returnInterest);
return returnInterest; return returnInterest;
} }
// tslint:disable-next-line:member-ordering
public interestObservable = new plugins.smartrx.ObservableIntake<Interest<DTInterestId, any>>(); public interestObservable = new plugins.smartrx.ObservableIntake<Interest<DTInterestId, any>>();
/** /**
* removes an interest from the interest map * removes an interest from the interest map
* @param objectArg removes an interest from the InterestMap
*/ */
public removeInterest(interestArg: Interest<DTInterestId, DTInterestFullfillment>) { public removeInterest(interestArg: Interest<DTInterestId, DTInterestFullfillment>) {
const interestToRemove = this.interestObjectMap.findOneAndRemoveSync((interestArg2) => { const interestToRemove = this.interestObjectMap.findOneAndRemoveSync((interestArg2) => {
@@ -117,13 +124,25 @@ export class InterestMap<DTInterestId, DTInterestFullfillment> {
/** /**
* finds an interest * finds an interest
* @param objectArg * @param interestId
*/ */
public findInterest(objectArg: DTInterestId): Interest<DTInterestId, DTInterestFullfillment> { public findInterest(interestId: DTInterestId): Interest<DTInterestId, DTInterestFullfillment> {
const comparableString = this.comparisonFunc(objectArg); const comparableString = this.comparisonFunc(interestId);
const interest = this.interestObjectMap.findSync((interestArg) => { const interest = this.interestObjectMap.findSync((interestArg) => {
return interestArg.comparisonString === comparableString; return interestArg.comparisonString === comparableString;
}); });
return interest; // if an interest is found, the interest is returned, otherwise interest is null return interest; // if an interest is found, the interest is returned, otherwise interest is null
} }
/**
* destroys the InterestMap and cleans up all resources
*/
public destroy() {
const interests = this.interestObjectMap.getArray();
for (const interest of interests) {
interest.destroy();
}
this.interestObjectMap.wipe();
this.interestObservable.signalComplete();
}
} }

View File

@@ -1,4 +1,4 @@
import * as plugins from './lik.plugins'; import * as plugins from './classes.plugins.js';
export class LimitedArray<T> { export class LimitedArray<T> {
array: T[] = []; array: T[] = [];

View File

@@ -1,6 +1,6 @@
import * as plugins from './lik.plugins'; import * as plugins from './classes.plugins.js';
import { ObjectMap } from './lik.objectmap'; import { ObjectMap } from './classes.objectmap.js';
export class LoopTracker<T> { export class LoopTracker<T> {
referenceObjectMap = new ObjectMap<any>(); referenceObjectMap = new ObjectMap<any>();
@@ -20,4 +20,18 @@ export class LoopTracker<T> {
return false; return false;
} }
} }
/**
* resets the loop tracker, clearing all tracked objects
*/
public reset() {
this.referenceObjectMap.wipe();
}
/**
* destroys the loop tracker and its underlying ObjectMap
*/
public destroy() {
this.referenceObjectMap.destroy();
}
} }

View File

@@ -1,5 +1,5 @@
import * as plugins from './lik.plugins'; import * as plugins from './classes.plugins.js';
import { FastMap } from './lik.fastmap'; import { FastMap } from './classes.fastmap.js';
export const uni = (prefix: string = 'uni') => { export const uni = (prefix: string = 'uni') => {
return `${prefix}xxxxxxxxxxx`.replace(/[xy]/g, (c) => { return `${prefix}xxxxxxxxxxx`.replace(/[xy]/g, (c) => {
@@ -62,8 +62,15 @@ export class ObjectMap<T> {
* remove key * remove key
* @param functionArg * @param functionArg
*/ */
public removeMappedUnique(uniqueKey: string) { public removeMappedUnique(uniqueKey: string): T {
const object = this.getMappedUnique(uniqueKey); const object = this.fastMap.removeFromMap(uniqueKey);
if (object !== undefined) {
this.eventSubject.next({
operation: 'remove',
payload: object,
});
}
return object;
} }
/** /**
@@ -85,7 +92,7 @@ export class ObjectMap<T> {
this.addMappedUnique(uniqueKey, objectArg); this.addMappedUnique(uniqueKey, objectArg);
this.eventSubject.next({ this.eventSubject.next({
operation: 'add', operation: 'add',
payload: objectArg payload: objectArg,
}); });
return uniqueKey; return uniqueKey;
} }
@@ -176,7 +183,7 @@ export class ObjectMap<T> {
const removedItem = this.fastMap.removeFromMap(keyToUse); const removedItem = this.fastMap.removeFromMap(keyToUse);
this.eventSubject.next({ this.eventSubject.next({
operation: 'remove', operation: 'remove',
payload: removedItem payload: removedItem,
}); });
return removedItem; return removedItem;
} }
@@ -209,7 +216,7 @@ export class ObjectMap<T> {
const removedObject = this.fastMap.removeFromMap(keyArg); const removedObject = this.fastMap.removeFromMap(keyArg);
this.eventSubject.next({ this.eventSubject.next({
operation: 'remove', operation: 'remove',
payload: removedObject payload: removedObject,
}); });
return removedObject; return removedObject;
} }
@@ -220,8 +227,13 @@ export class ObjectMap<T> {
* wipe Objectmap * wipe Objectmap
*/ */
public wipe() { public wipe() {
for (const keyArg of this.fastMap.getKeys()) { const keys = this.fastMap.getKeys();
this.fastMap.removeFromMap(keyArg); for (const keyArg of keys) {
const removedObject = this.fastMap.removeFromMap(keyArg);
this.eventSubject.next({
operation: 'remove',
payload: removedObject,
});
} }
} }
@@ -243,4 +255,12 @@ export class ObjectMap<T> {
public addAllFromOther(objectMapArg: ObjectMap<T>) { public addAllFromOther(objectMapArg: ObjectMap<T>) {
this.fastMap.addAllFromOther(objectMapArg.fastMap); this.fastMap.addAllFromOther(objectMapArg.fastMap);
} }
/**
* destroys the ObjectMap, completing the eventSubject and clearing all entries
*/
public destroy() {
this.wipe();
this.eventSubject.complete();
}
} }

17
ts/classes.plugins.ts Normal file
View File

@@ -0,0 +1,17 @@
// ==============
// @pushrocks
// ==============
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartmatch from '@push.rocks/smartmatch';
import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrx from '@push.rocks/smartrx';
import * as smarttime from '@push.rocks/smarttime';
export { smartdelay, smartmatch, smartpromise, smartrx, smarttime };
// ==============
// third party
// ==============
import symbolTree from 'symbol-tree';
export { symbolTree };

View File

@@ -1,4 +1,4 @@
import * as plugins from './lik.plugins'; import * as plugins from './classes.plugins.js';
/** /**
* allows you to easily keep track of a bunch of strings * allows you to easily keep track of a bunch of strings
@@ -116,4 +116,12 @@ export class Stringmap {
}); });
this._triggerUntilTrueFunctionArray = filteredArray; this._triggerUntilTrueFunctionArray = filteredArray;
} }
/**
* destroys the Stringmap, clearing all strings and pending triggers
*/
public destroy() {
this._stringArray = [];
this._triggerUntilTrueFunctionArray = [];
}
} }

View File

@@ -1,4 +1,4 @@
import * as plugins from './lik.plugins'; import * as plugins from './classes.plugins.js';
export interface ITimedAggregatorOptions<T> { export interface ITimedAggregatorOptions<T> {
aggregationIntervalInMillis: number; aggregationIntervalInMillis: number;
@@ -8,6 +8,7 @@ export interface ITimedAggregatorOptions<T> {
export class TimedAggregtor<T> { export class TimedAggregtor<T> {
public options: ITimedAggregatorOptions<T>; public options: ITimedAggregatorOptions<T>;
private storageArray: T[] = []; private storageArray: T[] = [];
private isStopped = false;
constructor(optionsArg: ITimedAggregatorOptions<T>) { constructor(optionsArg: ITimedAggregatorOptions<T>) {
this.options = optionsArg; this.options = optionsArg;
@@ -15,9 +16,16 @@ export class TimedAggregtor<T> {
private aggregationTimer: plugins.smarttime.Timer; private aggregationTimer: plugins.smarttime.Timer;
private checkAggregationStatus() { private checkAggregationStatus() {
if (this.isStopped) {
return;
}
const addAggregationTimer = () => { const addAggregationTimer = () => {
this.aggregationTimer = new plugins.smarttime.Timer(this.options.aggregationIntervalInMillis); this.aggregationTimer = new plugins.smarttime.Timer(this.options.aggregationIntervalInMillis);
this.aggregationTimer.completed.then(() => { this.aggregationTimer.completed.then(() => {
if (this.isStopped) {
this.aggregationTimer = null;
return;
}
const aggregateForProcessing = this.storageArray; const aggregateForProcessing = this.storageArray;
if (aggregateForProcessing.length === 0) { if (aggregateForProcessing.length === 0) {
this.aggregationTimer = null; this.aggregationTimer = null;
@@ -35,7 +43,29 @@ export class TimedAggregtor<T> {
} }
public add(aggregationArg: T) { public add(aggregationArg: T) {
if (this.isStopped) {
return;
}
this.storageArray.push(aggregationArg); this.storageArray.push(aggregationArg);
this.checkAggregationStatus(); this.checkAggregationStatus();
} }
/**
* stops the aggregation timer chain
* @param flushRemaining if true, calls functionForAggregation with any remaining items
*/
public stop(flushRemaining: boolean = false) {
this.isStopped = true;
if (this.aggregationTimer) {
this.aggregationTimer.reset();
this.aggregationTimer = null;
}
if (flushRemaining && this.storageArray.length > 0) {
const remaining = this.storageArray;
this.storageArray = [];
this.options.functionForAggregation(remaining);
} else {
this.storageArray = [];
}
}
} }

View File

@@ -1,4 +1,4 @@
import * as plugins from './lik.plugins'; import * as plugins from './classes.plugins.js';
export class Tree<T> { export class Tree<T> {
symbolTree: any; symbolTree: any;
@@ -95,7 +95,7 @@ export class Tree<T> {
} }
compareTreePosition(leftArg: T, rightArg: T): number { compareTreePosition(leftArg: T, rightArg: T): number {
return this.compareTreePosition(leftArg, rightArg); return this.symbolTree.compareTreePosition(leftArg, rightArg);
} }
remove(removeObjectArg: T): T { remove(removeObjectArg: T): T {

View File

@@ -1,9 +1,11 @@
export * from './lik.fastmap'; export * from './classes.asyncexecutionstack.js';
export * from './lik.interestmap'; export * from './classes.backpressuredarray.js';
export * from './lik.interestmap.interest'; export * from './classes.fastmap.js';
export * from './lik.limitedarray'; export * from './classes.interestmap.js';
export * from './lik.looptracker'; export * from './classes.interestmap.interest.js';
export * from './lik.objectmap'; export * from './classes.limitedarray.js';
export * from './lik.stringmap'; export * from './classes.looptracker.js';
export * from './lik.timedaggregator'; export * from './classes.objectmap.js';
export * from './lik.tree'; export * from './classes.stringmap.js';
export * from './classes.timedaggregator.js';
export * from './classes.tree.js';

View File

@@ -1,17 +0,0 @@
// ==============
// @pushrocks
// ==============
import * as smartdelay from '@pushrocks/smartdelay';
import * as smartmatch from '@pushrocks/smartmatch';
import * as smartpromise from '@pushrocks/smartpromise';
import * as smartrx from '@pushrocks/smartrx';
import * as smarttime from '@pushrocks/smarttime';
export { smartdelay, smartmatch, smartpromise, smartrx, smarttime };
// ==============
// third party
// ==============
const symbolTree = require('symbol-tree');
export { symbolTree };

14
tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false,
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"verbatimModuleSyntax": true
},
"exclude": [
"dist_*/**/*.d.ts"
]
}

View File

@@ -1,17 +0,0 @@
{
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"semicolon": [true, "always"],
"no-console": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"member-ordering": {
"options":{
"order": [
"static-method"
]
}
}
},
"defaultSeverity": "warning"
}