Compare commits

...

74 Commits

Author SHA1 Message Date
f1daec6f44 11.0.21 2024-06-23 23:55:55 +02:00
6698a583e9 fix(dependencies): Update dependencies to latest versions 2024-06-23 23:55:54 +02:00
bf52f01365 11.0.20 2024-06-07 17:13:08 +02:00
14adec5ba3 fix(core): update 2024-06-07 17:13:07 +02:00
fb2880c995 11.0.19 2024-06-07 12:24:54 +02:00
0e0ae7e42f fix(core): update 2024-06-07 12:24:53 +02:00
1391dbddbc 11.0.18 2024-06-06 23:33:36 +02:00
477736da82 fix(core): update 2024-06-06 23:33:35 +02:00
26a67d9662 11.0.17 2024-06-06 22:29:06 +02:00
14771fab27 fix(core): update 2024-06-06 22:29:06 +02:00
b0ae383622 update description 2024-05-29 14:13:08 +02:00
921f1cbb66 11.0.16 2024-05-28 12:43:47 +02:00
a9b373f5f8 fix(core): update 2024-05-28 12:43:46 +02:00
f0d48cc763 11.0.15 2024-05-17 17:49:57 +02:00
255cc844ad fix(core): update 2024-05-17 17:49:57 +02:00
50cc3fa8bf update tsconfig 2024-04-14 17:35:54 +02:00
21b3870a7d 11.0.14 2024-04-14 13:17:47 +02:00
023e1cdf29 fix(core): update 2024-04-14 13:17:46 +02:00
493a235065 11.0.13 2024-04-12 15:00:56 +02:00
e4e48dc9db fix(core): update 2024-04-12 15:00:55 +02:00
7de647daa1 11.0.12 2024-04-12 14:51:23 +02:00
34bc92137b fix(core): update 2024-04-12 14:51:23 +02:00
954df5a0ee 11.0.11 2024-04-12 14:44:13 +02:00
b9c15b11ad fix(core): update 2024-04-12 14:44:12 +02:00
55e6e81957 11.0.10 2024-04-03 15:39:27 +02:00
f37956270c fix(core): update 2024-04-03 15:39:26 +02:00
0c985b9c00 11.0.9 2024-04-03 15:38:59 +02:00
85ff487809 fix(core): update 2024-04-03 15:38:58 +02:00
eb65c4e859 11.0.8 2024-04-02 21:37:32 +02:00
a1d6c37f18 fix(core): update 2024-04-02 21:37:31 +02:00
915ad00801 11.0.7 2024-04-02 20:58:34 +02:00
910671bfc6 fix(core): update 2024-04-02 20:58:33 +02:00
ae8835d430 11.0.6 2024-04-02 20:53:03 +02:00
d08cc0f350 fix(core): update 2024-04-02 20:53:02 +02:00
1311039127 update npmextra.json: githost 2024-04-01 21:35:01 +02:00
c267d2f226 update npmextra.json: githost 2024-04-01 19:58:14 +02:00
5a9e1b5798 11.0.5 2024-04-01 17:46:40 +02:00
b1ec86ee2d fix(core): update 2024-04-01 17:46:40 +02:00
a1353170f6 update npmextra.json: githost 2024-03-30 21:47:12 +01:00
3ff6de201d 11.0.4 2023-11-24 19:29:00 +01:00
f5c106b2ca fix(core): update 2023-11-24 19:28:59 +01:00
d3c26d0d46 11.0.3 2023-11-24 19:15:41 +01:00
9935fe2d3c fix(core): update 2023-11-24 19:15:41 +01:00
3b05aab39b 11.0.2 2023-11-07 21:32:00 +01:00
53be2eb59d fix(core): update 2023-11-07 21:32:00 +01:00
c92a0dddbd 11.0.1 2023-11-07 14:09:49 +01:00
27403a73b5 fix(core): update 2023-11-07 14:09:48 +01:00
b925e5e662 11.0.0 2023-11-06 11:15:12 +01:00
98a5d2c94d BREAKING CHANGE(core): update 2023-11-06 11:15:11 +01:00
0e735cba20 10.0.40 2023-11-04 20:54:14 +01:00
f815457801 fix(core): update 2023-11-04 20:54:13 +01:00
f7e47ae354 10.0.39 2023-11-04 20:43:55 +01:00
684e893801 fix(core): update 2023-11-04 20:43:54 +01:00
d4b381d33d 10.0.38 2023-11-04 20:14:21 +01:00
291a11aa60 fix(core): update 2023-11-04 20:14:20 +01:00
ca592afec9 update 2023-11-04 20:07:43 +01:00
8b07197224 10.0.37 2023-11-03 02:31:57 +01:00
b60fd15ec6 fix(core): update 2023-11-03 02:31:57 +01:00
853eccc780 10.0.36 2023-11-03 02:24:37 +01:00
c26aff85b5 fix(core): update 2023-11-03 02:24:36 +01:00
321e4d9dea 10.0.35 2023-11-03 01:25:38 +01:00
3d2789857c fix(core): update 2023-11-03 01:25:37 +01:00
07b88a078d 10.0.34 2023-11-03 00:41:06 +01:00
6fee0028d8 fix(core): update 2023-11-03 00:41:05 +01:00
629c52f9bc 10.0.33 2023-10-12 02:21:40 +02:00
fd056c29e9 fix(core): update 2023-10-12 02:21:39 +02:00
36c456b509 10.0.32 2023-09-22 17:05:35 +02:00
16f8c25557 fix(core): update 2023-09-22 17:05:35 +02:00
219e070ba2 10.0.31 2023-08-31 18:45:24 +02:00
ee97e1d88b fix(core): update 2023-08-31 18:45:23 +02:00
279db74568 10.0.30 2023-08-23 10:58:38 +02:00
b84c504f11 fix(core): update 2023-08-23 10:58:38 +02:00
7b3194cc13 10.0.29 2023-08-23 09:38:49 +02:00
e1e821efec fix(core): update 2023-08-23 09:38:49 +02:00
25 changed files with 6619 additions and 3341 deletions

View File

@ -7,14 +7,13 @@ on:
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
@ -22,27 +21,27 @@ jobs:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3
- name: Install pnpm and npmci
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
- name: Install pnpm and npmci
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
- name: Run npm prepare
run: npmci npm prepare
- 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 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
- 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() }}
@ -52,16 +51,16 @@ jobs:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- 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
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build

View File

@ -7,14 +7,13 @@ on:
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
@ -22,27 +21,25 @@ jobs:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3
- name: Install pnpm and npmci
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- 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 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
- 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() }}
@ -52,19 +49,25 @@ jobs:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build
- 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
@ -74,12 +77,18 @@ jobs:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3
- name: Release
run: |
npmci node install stable
npmci npm publish
- 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
@ -90,21 +99,26 @@ jobs:
continue-on-error: true
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3
- name: Code quality
run: |
npmci command npm install -g typescript
npmci npm prepare
npmci npm install
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Trigger
run: npmci trigger
- name: Code quality
run: |
npmci command npm install -g typescript
npmci npm install
- name: Build docs
run: |
npmci node install stable
npmci npm install
pnpm install -g @gitzone/tsdoc
npmci command tsdoc
continue-on-error: true
- 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

276
changelog.md Normal file
View File

@ -0,0 +1,276 @@
# Changelog
## 2024-06-23 - 11.0.21 - fix(dependencies)
Update dependencies to latest versions
- Updated @push.rocks/smartpromise to ^4.0.4
- Updated @push.rocks/smartstream to ^3.0.44
- Updated glob to ^10.4.2
- Updated @types/node to ^20.14.8
## 2024-06-07 - 11.0.20 - Changelog
11.0.20
## 2024-06-07 - 11.0.19 - fix(core): update
11.0.19
- fix(core): update
## 2024-06-07 - 11.0.18 - fix(core): update
11.0.18
- fix(core): update
## 2024-06-06 - 11.0.17 - fix(core): update
11.0.17
- fix(core): update
## 2024-06-06 - 11.0.16 - fix(core): update
11.0.16
- fix(core): update
## 2024-05-29 - 11.0.16 - update description
11.0.16
- update description
## 2024-05-17 - 11.0.15 - fix(core): update
11.0.15
- fix(core): update
## 2024-04-14 - 11.0.14 - update tsconfig
11.0.14
- update tsconfig
## 2024-04-12 - 11.0.13 - fix(core): update
11.0.13
- fix(core): update
## 2024-04-12 - 11.0.12 - fix(core): update
11.0.12
- fix(core): update
## 2024-04-12 - 11.0.11 - fix(core): update
11.0.11
- fix(core): update
## 2024-04-03 - 11.0.10 - fix(core): update
11.0.10
- fix(core): update
## 2024-04-03 - 11.0.9 - fix(core): update
11.0.9
- fix(core): update
## 2024-04-02 - 11.0.8 - fix(core): update
11.0.8
- fix(core): update
## 2024-04-02 - 11.0.7 - fix(core): update
11.0.7
- fix(core): update
## 2024-04-02 - 11.0.6 - fix(core): update
11.0.6
- fix(core): update
## 2024-04-01 - 11.0.5 - update npmextra.json
11.0.5
- update npmextra.json: githost
## 2024-04-01 - 11.0.4 - fix(core): update
11.0.4
- fix(core): update
## 2023-11-24 - 11.0.3 - fix(core): update
11.0.3
- fix(core): update
## 2023-11-07 - 11.0.2 - fix(core): update
11.0.2
- fix(core): update
## 2023-11-07 - 11.0.1 - fix(core): update
11.0.1
- fix(core): update
## 2023-11-06 - 11.0.0 - fix(core): update
11.0.0
- fix(core): update
## 2023-11-06 - 10.0.40 - BREAKING CHANGE(core): update
10.0.40
- BREAKING CHANGE(core): update
## 2023-11-04 - 10.0.39 - fix(core): update
10.0.39
- fix(core): update
## 2023-11-04 - 10.0.38 - fix(core): update
10.0.38
- fix(core): update
## 2023-11-04 - 10.0.37 - fix(core): update
10.0.37
- fix(core): update
## 2023-11-03 - 10.0.36 - fix(core): update
10.0.36
- fix(core): update
## 2023-11-03 - 10.0.35 - fix(core): update
10.0.35
- fix(core): update
## 2023-11-03 - 10.0.34 - fix(core): update
10.0.34
- fix(core): update
## 2023-11-03 - 10.0.33 - fix(core): update
10.0.33
- fix(core): update
## 2023-10-12 - 10.0.32 - fix(core): update
10.0.32
- fix(core): update
## 2023-09-22 - 10.0.31 - fix(core): update
10.0.31
- fix(core): update
## 2023-08-31 - 10.0.30 - fix(core): update
10.0.30
- fix(core): update
## 2023-08-23 - 10.0.29 - fix(core): update
10.0.29
- fix(core): update
## 2023-07-12 - 10.0.28 - fix(core): update
10.0.28
- fix(core): update
## 2023-07-10 - 10.0.27 - fix(core): update
10.0.27
- fix(core): update
## 2023-07-10 - 10.0.26 - fix(core): update
10.0.26
- fix(core): update
## 2023-07-08 - 10.0.25 - fix(core): update
10.0.25
- fix(core): update
## 2023-06-25 - 10.0.24 to 10.0.14 - Series of Fixes
10.0.24 to 10.0.14
- Series of fixes in the core module
## 2023-01-09 - 10.0.13 to 10.0.6 - Series of Fixes
10.0.13 to 10.0.6
- Series of fixes in the core module
## 2022-09-05 - 10.0.5 to 10.0.3 - Series of Fixes
10.0.5 to 10.0.3
- Series of fixes in the core module
## 2022-06-07 - 10.0.2 to 10.0.1 - Series of Fixes
10.0.2 to 10.0.1
- Series of fixes in the core module
## 2022-06-07 - 9.0.7 - BREAKING CHANGE(core): switch to esm
9.0.7
- BREAKING CHANGE(core): switch to esm
## 2022-03-11 - 9.0.6 to 9.0.2 - Series of Fixes
9.0.6 to 9.0.2
- Series of fixes in the core module
## 2021-12-01 - 9.0.1 - fix(core): update
9.0.1
- fix(core): update
## 2021-12-01 - 9.0.0 - fix(absolute pathing)
9.0.0
- add functions for easily getting absolute paths
## 2021-11-30 - 8.0.11 - BREAKING CHANGE(relative pathing)
8.0.11
- improved relative pathing
## 2020-08-10 - 8.0.10 to 7.0.12 - Series of Fixes and Updates
8.0.10 to 7.0.12
- Series of fixes in the core module
- BREAKING CHANGE(Smartfile class): switch to a Buffer-only approach
## 2019-02-17 - 7.0.0 - fix(core): update dependencies
7.0.0
- fix(core): update dependencies
## 2019-01-27 - 6.0.12 - BREAKING CHANGE(smartfile.fs.fileExists)
6.0.12
- now returns a Promise<boolean>
## 2018-08-19 - 6.0.11 to 6.0.6 - Series of Fixes
6.0.11 to 6.0.6
- Series of fixes in core and dependencies
## 2018-07-03 - 6.0.5 to 5.0.0 - Series of Fixes
6.0.5 to 5.0.0
- Series of fixes in core and dependencies
## 2018-02-16 - 4.2.28 - BREAKING CHANGE(scope)
4.2.28
- switch to pushrocks scope

View File

@ -7,12 +7,42 @@
"gitzone": {
"projectType": "npm",
"module": {
"githost": "gitlab.com",
"githost": "code.foss.global",
"gitscope": "push.rocks",
"gitrepo": "smartfile",
"description": "smart ways to work with files in nodejs",
"description": "Provides comprehensive tools for efficient file management in Node.js using TypeScript, including handling streams, virtual directories, and various file operations.",
"npmPackagename": "@push.rocks/smartfile",
"license": "MIT"
"license": "MIT",
"keywords": [
"file management",
"TypeScript",
"Node.js",
"file operations",
"file manipulation",
"file streaming",
"virtual directory",
"filesystem utilities",
"memory-efficient file handling",
"custom file operations",
"write files",
"read files",
"copy files",
"delete files",
"list directories",
"handle large files",
"virtual filesystems",
"buffer operations"
]
}
},
"tsdoc": {
"classes": [
"SmartFile",
"StreamFile"
],
"descriptions": [
"the purpose of the StreamFile class is to provide a hybrid interface between streaming files and simple handling when writing and reading those files multiple times."
],
"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"
}
}

View File

@ -1,8 +1,8 @@
{
"name": "@push.rocks/smartfile",
"private": false,
"version": "10.0.28",
"description": "offers smart ways to work with files in nodejs",
"version": "11.0.21",
"description": "Provides comprehensive tools for efficient file management in Node.js using TypeScript, including handling streams, virtual directories, and various file operations.",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
"type": "module",
@ -13,42 +13,58 @@
},
"repository": {
"type": "git",
"url": "https://gitlab.com/pushrocks/smartfile.git"
"url": "https://code.foss.global/push.rocks/smartfile.git"
},
"keywords": [
"filesystem",
"json"
"file management",
"TypeScript",
"Node.js",
"file operations",
"file manipulation",
"file streaming",
"virtual directory",
"filesystem utilities",
"memory-efficient file handling",
"custom file operations",
"write files",
"read files",
"copy files",
"delete files",
"list directories",
"handle large files",
"virtual filesystems",
"buffer operations"
],
"author": "Lossless GmbH <hello@lossless.com> (https://lossless.com)",
"license": "MIT",
"bugs": {
"url": "https://gitlab.com/pushrocks/smartfile/issues"
"url": "https://gitlab.com/push.rocks/smartfile/issues"
},
"homepage": "https://gitlab.com/pushrocks/smartfile",
"homepage": "https://code.foss.global/push.rocks/smartfile",
"dependencies": {
"@push.rocks/lik": "^6.0.2",
"@push.rocks/smartdelay": "^3.0.1",
"@push.rocks/lik": "^6.0.15",
"@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartfile-interfaces": "^1.0.7",
"@push.rocks/smarthash": "^3.0.2",
"@push.rocks/smartjson": "^5.0.6",
"@push.rocks/smartmime": "^1.0.5",
"@push.rocks/smartpath": "^5.0.5",
"@push.rocks/smartpromise": "^4.0.2",
"@push.rocks/smartrequest": "^2.0.15",
"@push.rocks/smartstream": "^2.0.3",
"@types/fs-extra": "^11.0.1",
"@push.rocks/smarthash": "^3.0.4",
"@push.rocks/smartjson": "^5.0.20",
"@push.rocks/smartmime": "^2.0.2",
"@push.rocks/smartpath": "^5.0.18",
"@push.rocks/smartpromise": "^4.0.4",
"@push.rocks/smartrequest": "^2.0.22",
"@push.rocks/smartstream": "^3.0.44",
"@types/fs-extra": "^11.0.4",
"@types/glob": "^8.1.0",
"@types/js-yaml": "^4.0.5",
"fs-extra": "^11.1.1",
"glob": "^10.3.3",
"@types/js-yaml": "^4.0.9",
"fs-extra": "^11.2.0",
"glob": "^10.4.2",
"js-yaml": "^4.1.0"
},
"devDependencies": {
"@gitzone/tsbuild": "^2.1.66",
"@gitzone/tsrun": "^1.2.42",
"@gitzone/tstest": "^1.0.74",
"@pushrocks/tapbundle": "^5.0.8",
"@types/node": "^20.4.1"
"@git.zone/tsbuild": "^2.1.80",
"@git.zone/tsrun": "^1.2.46",
"@git.zone/tstest": "^1.0.90",
"@push.rocks/tapbundle": "^5.0.23",
"@types/node": "^20.14.8"
},
"files": [
"ts/**/*",

8349
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

1
readme.hints.md Normal file
View File

@ -0,0 +1 @@

264
readme.md
View File

@ -1,49 +1,241 @@
# @pushrocks/smartfile
smart ways to work with files in nodejs
# @push.rocks/smartfile
## Availabililty and Links
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartfile)
* [gitlab.com (source)](https://gitlab.com/pushrocks/smartfile)
* [github.com (source mirror)](https://github.com/pushrocks/smartfile)
* [docs (typedoc)](https://pushrocks.gitlab.io/smartfile/)
> Provides a robust suite of tools for managing files in Node.js using TypeScript.
## Status for master
## Install
Status Category | Status Badge
-- | --
GitLab Pipelines | [![pipeline status](https://gitlab.com/pushrocks/smartfile/badges/master/pipeline.svg)](https://lossless.cloud)
GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/pushrocks/smartfile/badges/master/coverage.svg)](https://lossless.cloud)
npm | [![npm downloads per month](https://badgen.net/npm/dy/@pushrocks/smartfile)](https://lossless.cloud)
Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/pushrocks/smartfile)](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/smartfile)](https://lossless.cloud)
PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@pushrocks/smartfile)](https://lossless.cloud)
BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@pushrocks/smartfile)](https://lossless.cloud)
To integrate `@push.rocks/smartfile` into your project, run:
```bash
npm install @push.rocks/smartfile
```
## Usage
smartfile is an approach of being one tool to handle files in diverse environments.
`@push.rocks/smartfile` offers extensive file management utilities, enabling seamless file processing with TypeScript in a Node.js environment. Below are detailed examples showcasing various features of the module.
### Smartfile Sections
### Quick Start
smartfile thinks in sections:
First, ensure you're working in an environment that supports ECMAScript modules (ESM) and TypeScript. Heres how youd generally import and use `@push.rocks/smartfile`:
| section | description |
| ----------- | ---------------------------------------------------------------------------- |
| fs | (object) gets data from fs to somewhere |
| memory | gets data from memory to somewhere |
| remote | gets data from remote locations to somewhere |
| interpreter | (object) handles yaml and json |
| smartfile | (class) a virtual representation of a file, alternative to vinyl file format |
```typescript
import { SmartFile, StreamFile, VirtualDirectory, fs, memory, interpreter } from '@push.rocks/smartfile';
```
## Contribution
### Working with `SmartFile`
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). :)
#### Reading Files
For further information read the linked docs at the top of this readme.
To read from a file and convert it to a `SmartFile` instance:
## Legal
> MIT licensed | **&copy;** [Task Venture Capital GmbH](https://task.vc)
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
```typescript
const myJsonSmartFile: SmartFile = await SmartFile.fromFilePath('./somePath/data.json');
const jsonData = JSON.parse(myJsonSmartFile.contents.toString());
console.log(jsonData); // Assuming the file contains JSON content
```
#### Writing Files
To write data to a file through a `SmartFile`:
```typescript
const filePath: string = './output/outputData.json';
const content: string = JSON.stringify({ key: 'value' });
await memory.toFs(content, filePath);
```
### Streaming Large Files with `StreamFile`
When dealing with large files, you can use `StreamFile` to handle such files efficiently, minimizing memory usage:
```typescript
const largeFile: StreamFile = await StreamFile.fromPath('./largeInput/largeFile.mp4');
await largeFile.writeToDisk('./largeOutput/largeFileCopy.mp4');
```
### Working with Virtual Directories
Handling multiple files as if they were part of a file system:
```typescript
const virtualDir = await VirtualDirectory.fromFsDirPath('./data/inputDir');
await virtualDir.saveToDisk('./data/outputDir');
```
### File System Operations
`@push.rocks/smartfile` provides a suite of utilities for common file system operations such as copying, deleting, and listing files or directories.
#### Copying a File
```typescript
await fs.copy('./sourceFile.txt', './destinationFile.txt');
```
#### Deleting a Directory
```typescript
await fs.remove('./directoryToDelete');
```
#### Listing Files in a Directory
```typescript
const fileList: string[] = await fs.listFiles('./someDirectory');
console.log(fileList);
```
### Advanced File Management
For specialized file operations, such as editing the contents of a file or streaming files from URLs, `@push.rocks/smartfile` includes advanced management features.
#### Editing a Files Contents
```typescript
const smartFileToEdit: SmartFile = await SmartFile.fromFilePath('./editableFile.txt');
await smartFileToEdit.editContentAsString(async (content) => content.replace(/originalText/g, 'newText'));
await smartFileToEdit.write();
```
#### Streaming a File from a URL
```typescript
const streamedFile: StreamFile = await StreamFile.fromUrl('https://example.com/file.pdf');
await streamedFile.writeToDisk('./downloadedFiles/file.pdf');
```
### Working with File Buffers and Streams
`@push.rocks/smartfile` allows you to easily work with files using Buffers and Streams, facilitating operations like file transformations, uploads, and downloads.
#### Creating a SmartFile from a Buffer
```typescript
const buffer: Buffer = Buffer.from('Sample data');
const bufferSmartFile: SmartFile = await SmartFile.fromBuffer('./bufferFile.txt', buffer);
await bufferSmartFile.write();
```
### Using `VirtualDirectory` for Complex File Management
`VirtualDirectory` simplifies the management of multiple files that are otherwise scattered across different directories or created at runtime.
#### Creating a `VirtualDirectory`
To create a `VirtualDirectory` from an existing file directory:
```typescript
const virtualDirectory = await VirtualDirectory.fromFsDirPath('./someDirectory');
```
#### Adding More Files
You can add more `SmartFile` instances to your `VirtualDirectory`:
```typescript
const additionalFiles = [
await SmartFile.fromFilePath('./anotherDirectory/file1.txt'),
await SmartFile.fromFilePath('./anotherDirectory/file2.txt')
];
virtualDirectory.addSmartfiles(additionalFiles);
```
#### Saving `VirtualDirectory` to Disk
Save your entire `VirtualDirectory` to disk:
```typescript
await virtualDirectory.saveToDisk('./outputDirectory');
```
### Utilizing StreamFile for Efficient File Handling
Using `StreamFile` can be especially beneficial for large files or when performing streaming operations.
#### Streaming from a URL
`StreamFile` provides capabilities to stream files directly from URLs, making it easier to work with remote content.
```typescript
const urlStreamFile: StreamFile = await StreamFile.fromUrl('https://example.com/largefile.zip');
await urlStreamFile.writeToDisk('./downloadedFiles/largefile.zip');
```
### Combining Buffer and Stream Approaches
Create `StreamFile` from a buffer for efficient, multi-use streams.
```typescript
const buffer = Buffer.from('Streaming buffer content');
const bufferStreamFile = StreamFile.fromBuffer(buffer, 'bufferBasedStream.txt');
await bufferStreamFile.writeToDisk('./streams/bufferBasedStream.txt');
```
### Read and Write Operations with StreamFile
Perform read and write operations efficiently using `StreamFile`.
```typescript
const fileStream = await StreamFile.fromPath('./inputData/largeFile.data');
await fileStream.writeToDisk('./outputData/largeFileCopy.data');
```
Check for completeness of your read and write operations, ensuring the integrity of file content.
```typescript
const readBuffer = await fileStream.getContentAsBuffer();
console.log(readBuffer.toString());
```
### Ensuring File Readiness for Streaming
Ensure a file is ready for streaming or create a custom readable stream incorporating data transformation.
```typescript
const smartReadStream = new SmartReadStream('./incomingData/sample.data');
smartReadStream.on('data', (chunk) => {
console.log('New Data Chunk:', chunk.toString());
});
```
### File Transformation with SmartReadStream
Perform transformations on the stream of data while reading:
```typescript
smartReadStream.on('data', (chunk) => {
// Perform some transformation
const transformedChunk = chunk.toString().toUpperCase();
console.log('Transformed Data Chunk:', transformedChunk);
});
```
### Streaming with SmartReadStream
Stream data from a `SmartReadStream` to a file efficiently managing large datasets.
```typescript
const transformedWriteStream = fs.createWriteStream('./processedData/transformed.data');
smartReadStream.pipe(transformedWriteStream);
```
`@push.rocks/smartfile` significantly simplifies the handling of complex file operations in Node.js projects, making these tasks straightforward while maintaining efficiency and ease of use. Explore and leverage these features to enhance your project's file management capabilities.
## 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.

67
test/test.streamfile.ts Normal file
View File

@ -0,0 +1,67 @@
import * as path from 'path';
import { expect, tap } from '@push.rocks/tapbundle';
import * as smartfile from '../ts/index.js'; // adjust the import path as needed
// Test assets path
const testAssetsPath = './test/testassets/';
// ---------------------------
// StreamFile tests
// ---------------------------
tap.test('StreamFile.fromPath should create a StreamFile from a file path', async () => {
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
const contentBuffer = await streamFile.getContentAsBuffer();
expect(contentBuffer).toBeInstanceOf(Buffer);
});
tap.test('StreamFile.fromUrl should create a StreamFile from a URL', async () => {
const streamFile = await smartfile.StreamFile.fromUrl('http://example.com/somefile.json');
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
});
tap.test('StreamFile.fromBuffer should create a StreamFile from a Buffer', async () => {
const buffer = Buffer.from('Some content');
const streamFile = smartfile.StreamFile.fromBuffer(buffer, 'bufferfile.txt');
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
});
tap.test('StreamFile should write the stream to disk', async () => {
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
await streamFile.writeToDisk(path.join(testAssetsPath, 'temp', 'mytest.json'));
// Verify the file was written
expect(
// We'll use the fileExists method from your smartfile library
// Replace with the actual method you use to check file existence
await smartfile.fs.fileExists(path.join(testAssetsPath, 'temp', 'mytest.json'))
).toBeTrue();
});
tap.test('StreamFile should write to a directory', async () => {
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
await streamFile.writeToDir(path.join(testAssetsPath, 'temp'));
// Verify the file was written
expect(
await smartfile.fs.fileExists(path.join(testAssetsPath, 'temp', 'mytest.json'))
).toBeTrue();
});
tap.test('StreamFile should return content as a buffer', async () => {
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
const contentBuffer = await streamFile.getContentAsBuffer();
expect(contentBuffer).toBeInstanceOf(Buffer);
// Further checks on the content can be added here if necessary
});
tap.test('StreamFile should return content as a string', async () => {
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
const contentString = await streamFile.getContentAsString();
expect(typeof contentString).toBeTypeofString();
// Verify the content matches what's expected
// This assumes the file contains a JSON object with a key 'key1' with value 'this works'
expect(JSON.parse(contentString).key1).toEqual('this works');
});
// Start the test sequence
tap.start();

View File

@ -1,7 +1,7 @@
import * as smartfile from '../ts/index.js';
import * as path from 'path';
import { expect, tap } from '@pushrocks/tapbundle';
import { expect, tap } from '@push.rocks/tapbundle';
// ---------------------------
// smartfile.fs
@ -64,7 +64,7 @@ tap.test('.fs.fileTreeToObject -> should read a file tree into an Object', async
path.resolve('./test/testassets/'),
'**/*.txt'
);
expect(fileArrayArg[0]).toBeInstanceOf(smartfile.Smartfile);
expect(fileArrayArg[0]).toBeInstanceOf(smartfile.SmartFile);
expect(fileArrayArg[0].contents.toString()).toEqual(fileArrayArg[0].contentBuffer.toString());
});
@ -175,7 +175,7 @@ tap.test('.Smartfile -> should produce vinyl compatible files', async () => {
'./test/testassets/testfolder/**/*'
);
const localSmartfile = smartfileArray[0];
expect(localSmartfile).toBeInstanceOf(smartfile.Smartfile);
expect(localSmartfile).toBeInstanceOf(smartfile.SmartFile);
expect(localSmartfile.contents).toBeInstanceOf(Buffer);
// tslint:disable-next-line:no-unused-expression
expect(localSmartfile.isBuffer()).toBeTrue();
@ -202,9 +202,9 @@ tap.test('should output a smartfile array to disk', async () => {
tap.test('should create, store and retrieve valid smartfiles', async () => {
const fileString = 'hi there';
const filePath = './test/testassets/utf8.txt';
const smartfileInstance = await smartfile.Smartfile.fromString(filePath, fileString, 'utf8');
const smartfileInstance = await smartfile.SmartFile.fromString(filePath, fileString, 'utf8');
smartfileInstance.write();
const smartfileInstance2 = await smartfile.Smartfile.fromFilePath(filePath);
const smartfileInstance2 = await smartfile.SmartFile.fromFilePath(filePath);
const retrievedString = smartfileInstance.contents.toString();
expect(retrievedString).toEqual(fileString);
});
@ -212,7 +212,7 @@ tap.test('should create, store and retrieve valid smartfiles', async () => {
tap.test('should get a hash', async () => {
const fileString = 'hi there';
const filePath = './test/testassets/utf8.txt';
const smartfileInstance = await smartfile.Smartfile.fromString(filePath, fileString, 'utf8');
const smartfileInstance = await smartfile.SmartFile.fromString(filePath, fileString, 'utf8');
const hash = await smartfileInstance.getHash();
console.log(hash);
});

View File

@ -1,4 +1,4 @@
import { tap, expect } from '@pushrocks/tapbundle';
import { tap, expect } from '@push.rocks/tapbundle';
import * as smartfile from '../ts/index.js';

View File

@ -0,0 +1,8 @@
{
"key1": "this works",
"key2": "this works too",
"key3": {
"nestedkey1": "hello"
}
}

View File

@ -0,0 +1,8 @@
{
"key1": "this works",
"key2": "this works too",
"key3": {
"nestedkey1": "hello"
}
}

View File

@ -1,8 +1,8 @@
/**
* autocreated commitinfo by @pushrocks/commitinfo
* autocreated commitinfo by @push.rocks/commitinfo
*/
export const commitinfo = {
name: '@push.rocks/smartfile',
version: '10.0.28',
description: 'offers smart ways to work with files in nodejs'
version: '11.0.21',
description: 'Provides comprehensive tools for efficient file management in Node.js using TypeScript, including handling streams, virtual directories, and various file operations.'
}

View File

@ -1,6 +1,6 @@
import * as plugins from './smartfile.plugins.js';
import * as fs from './smartfile.fs.js';
import * as memory from './smartfile.memory.js';
import * as plugins from './plugins.js';
import * as fs from './fs.js';
import * as memory from './memory.js';
export interface ISmartfileConstructorOptions {
path: string;
@ -9,10 +9,9 @@ export interface ISmartfileConstructorOptions {
}
/**
* class Smartfile
* -> is vinyl file compatible
* an vinyl file compatible in memory file class
*/
export class Smartfile extends plugins.smartjson.Smartjson {
export class SmartFile extends plugins.smartjson.Smartjson {
// ======
// STATIC
// ======
@ -24,7 +23,7 @@ export class Smartfile extends plugins.smartjson.Smartjson {
public static async fromFilePath(filePath: string, baseArg: string = process.cwd()) {
filePath = plugins.path.resolve(filePath);
const fileBuffer = fs.toBufferSync(filePath);
const smartfile = new Smartfile({
const smartfile = new SmartFile({
contentBuffer: fileBuffer,
base: baseArg,
path: plugins.path.relative(baseArg, filePath),
@ -37,7 +36,7 @@ export class Smartfile extends plugins.smartjson.Smartjson {
contentBufferArg: Buffer,
baseArg: string = process.cwd()
) {
const smartfile = new Smartfile({
const smartfile = new SmartFile({
contentBuffer: contentBufferArg,
base: baseArg,
path: plugins.path.relative(baseArg, filePath),
@ -52,7 +51,7 @@ export class Smartfile extends plugins.smartjson.Smartjson {
encodingArg: 'utf8' | 'binary',
baseArg = process.cwd()
) {
const smartfile = new Smartfile({
const smartfile = new SmartFile({
contentBuffer: Buffer.from(contentStringArg, encodingArg),
base: baseArg,
path: plugins.path.relative(baseArg, filePath),
@ -62,7 +61,40 @@ export class Smartfile extends plugins.smartjson.Smartjson {
}
public static async fromFoldedJson(foldedJsonArg: string) {
return new Smartfile(plugins.smartjson.parse(foldedJsonArg));
return new SmartFile(plugins.smartjson.parse(foldedJsonArg));
}
/**
* creates a Smartfile from a ReadableStream
* @param stream a readable stream that provides file content
* @param filePath the file path to associate with the content
* @param baseArg the base path to use for the file
*/
public static async fromStream(
stream: plugins.stream.Readable,
filePath: string,
baseArg: string = process.cwd()
): Promise<SmartFile> {
return new Promise<SmartFile>((resolve, reject) => {
const chunks: Buffer[] = [];
stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
stream.on('error', (error) => reject(error));
stream.on('end', () => {
const contentBuffer = Buffer.concat(chunks);
const smartfile = new SmartFile({
contentBuffer: contentBuffer,
base: baseArg,
path: plugins.path.relative(baseArg, filePath),
});
resolve(smartfile);
});
});
}
public static async fromUrl (urlArg: string) {
const response = await plugins.smartrequest.getBinary(urlArg);
const smartfile = await SmartFile.fromBuffer(urlArg, response.body);
return smartfile;
}
// ========
@ -128,7 +160,7 @@ export class Smartfile extends plugins.smartjson.Smartjson {
* @param contentString
*/
public setContentsFromString(contentString: string, encodingArg: 'utf8' | 'binary' = 'utf8') {
this.contents = new Buffer(contentString, encodingArg);
this.contents = Buffer.from(contentString, encodingArg);
}
/**
@ -137,7 +169,9 @@ export class Smartfile extends plugins.smartjson.Smartjson {
* - no argument write to exactly where the file was picked up
*/
public async write() {
await memory.toFs(this.contentBuffer, plugins.path.join(this.base, this.path));
let writePath = plugins.smartpath.transform.makeAbsolute(this.path, this.base);
console.log(`writing to ${writePath}`);
await memory.toFs(this.contentBuffer, writePath);
}
/**
@ -262,4 +296,21 @@ export class Smartfile extends plugins.smartjson.Smartjson {
const newFileString = await editFuncArg(this.contentBuffer.toString());
this.contentBuffer = Buffer.from(newFileString);
}
/**
* Returns a ReadableStream from the file's content buffer
*/
public getStream(): plugins.stream.Readable {
const stream = new plugins.stream.Readable();
stream.push(this.contentBuffer); // Push the content buffer to the stream
stream.push(null); // Push null to signify the end of the stream (EOF)
return stream;
}
/**
* Returns the size of the file in bytes
*/
public async getSize(): Promise<number> {
return this.contentBuffer.length;
}
}

174
ts/classes.streamfile.ts Normal file
View File

@ -0,0 +1,174 @@
import * as plugins from './plugins.js';
import * as smartfileFs from './fs.js';
import * as smartfileFsStream from './fsstream.js';
import { Readable } from 'stream';
type TStreamSource = (streamFile: StreamFile) => Promise<Readable>;
/**
* The StreamFile class represents a file as a stream.
* It allows creating streams from a file path, a URL, or a buffer.
*/
export class StreamFile {
// STATIC
public static async fromPath(filePath: string): Promise<StreamFile> {
const streamSource: TStreamSource = async (streamFileArg) => smartfileFsStream.createReadStream(filePath);
const streamFile = new StreamFile(streamSource, filePath);
streamFile.multiUse = true;
streamFile.byteLengthComputeFunction = async () => {
const stats = await smartfileFs.stat(filePath);
return stats.size;
}
return streamFile;
}
public static async fromUrl(url: string): Promise<StreamFile> {
const streamSource: TStreamSource = async (streamFileArg) => plugins.smartrequest.getStream(url); // Replace with actual plugin method
const streamFile = new StreamFile(streamSource);
streamFile.multiUse = true;
streamFile.byteLengthComputeFunction = async () => {
const response = await plugins.smartrequest.getBinary(url); // TODO: switch to future .getBinaryByteLength()
return response.body.length;
}
return streamFile;
}
public static fromBuffer(buffer: Buffer, relativeFilePath?: string): StreamFile {
const streamSource: TStreamSource = async (streamFileArg) => {
const stream = new Readable();
stream.push(buffer);
stream.push(null); // End of stream
return stream;
};
const streamFile = new StreamFile(streamSource, relativeFilePath);
streamFile.multiUse = true;
streamFile.byteLengthComputeFunction = async () => buffer.length;
return streamFile;
}
/**
* Creates a StreamFile from an existing Readable stream with an option for multiple uses.
* @param stream A Node.js Readable stream.
* @param relativeFilePath Optional file path for the stream.
* @param multiUse If true, the stream can be read multiple times, caching its content.
* @returns A StreamFile instance.
*/
public static fromStream(stream: Readable, relativeFilePath?: string, multiUse: boolean = false): StreamFile {
const streamSource: TStreamSource = (streamFileArg) => {
if (streamFileArg.multiUse) {
// If multi-use is enabled and we have cached content, create a new readable stream from the buffer
const bufferedStream = new Readable();
bufferedStream.push(streamFileArg.cachedStreamBuffer);
bufferedStream.push(null); // No more data to push
return Promise.resolve(bufferedStream);
} else {
return Promise.resolve(stream);
}
};
const streamFile = new StreamFile(streamSource, relativeFilePath);
streamFile.multiUse = multiUse;
// If multi-use is enabled, cache the stream when it's first read
if (multiUse) {
const chunks: Buffer[] = [];
stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
stream.on('end', () => {
streamFile.cachedStreamBuffer = Buffer.concat(chunks);
});
// It's important to handle errors that may occur during streaming
stream.on('error', (err) => {
console.error('Error while caching stream:', err);
});
}
return streamFile;
}
// INSTANCE
relativeFilePath?: string;
private streamSource: TStreamSource;
// enable stream based multi use
private cachedStreamBuffer?: Buffer;
public multiUse: boolean;
public used: boolean = false;
public byteLengthComputeFunction: () => Promise<number>;
private constructor(streamSource: TStreamSource, relativeFilePath?: string) {
this.streamSource = streamSource;
this.relativeFilePath = relativeFilePath;
}
// METHODS
private checkMultiUse() {
if (!this.multiUse && this.used) {
throw new Error('This stream can only be used once.');
}
this.used = true;
}
/**
* Creates a new readable stream from the source.
*/
public async createReadStream(): Promise<Readable> {
return this.streamSource(this);
}
/**
* Writes the stream to the disk at the specified path.
* @param filePathArg The file path where the stream should be written.
*/
public async writeToDisk(filePathArg: string): Promise<void> {
this.checkMultiUse();
const readStream = await this.createReadStream();
const writeStream = smartfileFsStream.createWriteStream(filePathArg);
return new Promise((resolve, reject) => {
readStream.pipe(writeStream);
readStream.on('error', reject);
writeStream.on('error', reject);
writeStream.on('finish', resolve);
});
}
public async writeToDir(dirPathArg: string) {
this.checkMultiUse();
const filePath = plugins.path.join(dirPathArg, this.relativeFilePath);
await smartfileFs.ensureDir(plugins.path.parse(filePath).dir);
return this.writeToDisk(filePath);
}
public async getContentAsBuffer() {
this.checkMultiUse();
const done = plugins.smartpromise.defer<Buffer>();
const readStream = await this.createReadStream();
const chunks: Buffer[] = [];
readStream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
readStream.on('error', done.reject);
readStream.on('end', () => {
const contentBuffer = Buffer.concat(chunks);
done.resolve(contentBuffer);
});
return done.promise;
}
public async getContentAsString(formatArg: 'utf8' | 'binary' = 'utf8') {
const contentBuffer = await this.getContentAsBuffer();
return contentBuffer.toString(formatArg);
}
/**
* Returns the size of the file content in bytes.
*/
public async getSize(): Promise<number> {
if (this.byteLengthComputeFunction) {
return this.byteLengthComputeFunction();
} else {
return null;
}
}
}

View File

@ -1,11 +1,21 @@
import { Smartfile } from './smartfile.classes.smartfile.js';
import * as plugins from './smartfile.plugins.js';
import * as fs from './smartfile.fs.js';
import { SmartFile } from './classes.smartfile.js';
import * as plugins from './plugins.js';
import * as fs from './fs.js';
export interface IVirtualDirectoryConstructorOptions {
mode: ''
}
/**
* a virtual directory exposes a fs api
*/
export class VirtualDirectory {
consstructor(options = {}) {
}
// STATIC
public static async fromFsDirPath(pathArg: string): Promise<VirtualDirectory> {
const newVirtualDir = new VirtualDirectory();
@ -18,17 +28,17 @@ export class VirtualDirectory {
): Promise<VirtualDirectory> {
const newVirtualDir = new VirtualDirectory();
for (const fileArg of virtualDirTransferableObjectArg.files) {
newVirtualDir.addSmartfiles([Smartfile.enfoldFromJson(fileArg) as Smartfile]);
newVirtualDir.addSmartfiles([SmartFile.enfoldFromJson(fileArg) as SmartFile]);
}
return newVirtualDir;
}
// INSTANCE
public smartfileArray: Smartfile[] = [];
public smartfileArray: SmartFile[] = [];
constructor() {}
public addSmartfiles(smartfileArrayArg: Smartfile[]) {
public addSmartfiles(smartfileArrayArg: SmartFile[]) {
this.smartfileArray = this.smartfileArray.concat(smartfileArrayArg);
}

View File

@ -1,9 +1,10 @@
import * as plugins from './smartfile.plugins.js';
import * as interpreter from './smartfile.interpreter.js';
import * as plugins from './plugins.js';
import * as interpreter from './interpreter.js';
import { Smartfile } from './smartfile.classes.smartfile.js';
import { SmartFile } from './classes.smartfile.js';
import * as memory from './smartfile.memory.js';
import * as memory from './memory.js';
import type { StreamFile } from './classes.streamfile.js';
/*===============================================================
============================ Checks =============================
===============================================================*/
@ -40,7 +41,18 @@ export const fileExists = async (filePath): Promise<boolean> => {
/**
* Checks if given path points to an existing directory
*/
export const isDirectory = (pathArg): boolean => {
export const isDirectory = (pathArg: string): boolean => {
try {
return plugins.fsExtra.statSync(pathArg).isDirectory();
} catch (err) {
return false;
}
};
/**
* Checks if given path points to an existing directory
*/
export const isDirectorySync = (pathArg: string): boolean => {
try {
return plugins.fsExtra.statSync(pathArg).isDirectory();
} catch (err) {
@ -178,7 +190,8 @@ export const removeManySync = (filePathArrayArg: string[]): void => {
===============================================================*/
/**
*
* reads a file content to an object
* good for JSON, YAML, TOML, etc.
* @param filePathArg
* @param fileTypeArg
* @returns {any}
@ -194,7 +207,7 @@ export const toObjectSync = (filePathArg, fileTypeArg?) => {
* reads a file content to a String
*/
export const toStringSync = (filePath: string): string => {
const encoding = plugins.smartmime.getEncoding(filePath);
const encoding = plugins.smartmime.getEncodingForPathSync(filePath);
let fileString: string | Buffer = plugins.fsExtra.readFileSync(filePath, encoding);
if (Buffer.isBuffer(fileString)) {
fileString = fileString.toString('binary');
@ -210,6 +223,18 @@ export const toBufferSync = (filePath: string): Buffer => {
return plugins.fsExtra.readFileSync(filePath);
};
/**
* Creates a Readable Stream from a file path.
* @param filePath The path to the file.
* @returns {fs.ReadStream}
*/
export const toReadStream = (filePath: string): plugins.fs.ReadStream => {
if (!fileExistsSync(filePath)) {
throw new Error(`File does not exist at path: ${filePath}`);
}
return plugins.fsExtra.createReadStream(filePath);
};
export const fileTreeToHash = async (dirPathArg: string, miniMatchFilter: string) => {
const fileTreeObject = await fileTreeToObject(dirPathArg, miniMatchFilter);
let combinedString = '';
@ -235,7 +260,7 @@ export const fileTreeToObject = async (dirPathArg: string, miniMatchFilter: stri
}
const fileTree = await listFileTree(dirPath, miniMatchFilter);
const smartfileArray: Smartfile[] = [];
const smartfileArray: SmartFile[] = [];
for (const filePath of fileTree) {
const readPath = ((): string => {
if (!plugins.path.isAbsolute(filePath)) {
@ -244,12 +269,12 @@ export const fileTreeToObject = async (dirPathArg: string, miniMatchFilter: stri
return filePath;
}
})();
const fileContentString = toStringSync(readPath);
const fileBuffer = plugins.fs.readFileSync(readPath);
// push a read file as Smartfile
smartfileArray.push(
new Smartfile({
contentBuffer: Buffer.from(fileContentString),
new SmartFile({
contentBuffer: fileBuffer,
base: dirPath,
path: filePath,
})
@ -366,27 +391,101 @@ export const listFileTree = async (
};
/**
* checks wether a file is ready for processing
* Watches for file stability before resolving the promise.
*/
export const waitForFileToBeReady = async (filePathArg: string): Promise<void> => {
if (!plugins.path.isAbsolute(filePathArg)) {
filePathArg = plugins.path.resolve(filePathArg);
}
const limitedArray = new plugins.lik.LimitedArray<number>(3);
let fileReady = false;
while (!fileReady) {
const stats = await plugins.fsExtra.stat(filePathArg);
limitedArray.addOne(stats.size);
if (
limitedArray.array.length < 3 ||
!(
limitedArray.array[0] === limitedArray.array[1] &&
limitedArray.array[1] === limitedArray.array[2]
)
) {
await plugins.smartdelay.delayFor(5000);
} else {
fileReady = true;
export const waitForFileToBeReady = (filePathArg: string): Promise<void> => {
return new Promise(async (resolve, reject) => {
let lastFileSize = -1;
let fileIsStable = false;
const checkFileStability = async () => {
let currentFileSize: number;
const deferred = plugins.smartpromise.defer();
plugins.fs.stat(filePathArg, (err, stats) => {
if (err) {
fileIsStable = true;
watcher.close();
reject(err);
return;
}
currentFileSize = stats.size;
deferred.resolve();
});
await deferred.promise;
if (currentFileSize === lastFileSize) {
fileIsStable = true;
await plugins.smartdelay.delayFor(100);
resolve();
}
lastFileSize = currentFileSize;
};
const watcher = plugins.fs.watch(filePathArg, (eventType, filename) => {
if (eventType === 'change') {
checkFileStability();
}
});
watcher.on('error', (error) => {
watcher.close();
reject(error);
});
while (!fileIsStable) {
await checkFileStability();
if (!fileIsStable) {
await plugins.smartdelay.delayFor(5000);
}
}
}
watcher.close();
});
};
/**
* writes string or Smartfile to disk.
* @param fileArg
* @param fileNameArg
* @param fileBaseArg
*/
export let toFs = async (
fileContentArg: string | Buffer | SmartFile | StreamFile,
filePathArg: string,
optionsArg: {
respectRelative?: boolean;
} = {}
) => {
const done = plugins.smartpromise.defer();
// check args
if (!fileContentArg || !filePathArg) {
throw new Error('expected valid arguments');
}
// prepare actual write action
let fileContent: string | Buffer;
let fileEncoding: 'utf8' | 'binary' = 'utf8';
let filePath: string = filePathArg;
// handle Smartfile
if (fileContentArg instanceof SmartFile) {
fileContent = fileContentArg.contentBuffer;
// handle options
if (optionsArg.respectRelative) {
filePath = plugins.path.join(filePath, fileContentArg.path);
}
} else if (Buffer.isBuffer(fileContentArg)) {
fileContent = fileContentArg;
fileEncoding = 'binary';
} else if (typeof fileContentArg === 'string') {
fileContent = fileContentArg;
} else {
throw new Error('fileContent is neither string nor Smartfile');
}
await ensureDir(plugins.path.parse(filePath).dir);
plugins.fsExtra.writeFile(filePath, fileContent, { encoding: fileEncoding }, done.resolve);
return await done.promise;
};
export const stat = async (filePathArg: string) => {
return plugins.fsPromises.stat(filePathArg);
}

195
ts/fsstream.ts Normal file
View File

@ -0,0 +1,195 @@
/*
This file contains logic for streaming things from and to the filesystem
*/
import * as plugins from './plugins.js';
export const createReadStream = (pathArg: string) => {
return plugins.fs.createReadStream(pathArg);
};
export const createWriteStream = (pathArg: string) => {
return plugins.fs.createWriteStream(pathArg);
};
export const processFile = async (
filePath: string,
asyncFunc: (fileStream: plugins.stream.Readable) => Promise<void>
): Promise<void> => {
return new Promise((resolve, reject) => {
const fileStream = createReadStream(filePath);
asyncFunc(fileStream).then(resolve).catch(reject);
});
}
export const processDirectory = async (
directoryPath: string,
asyncFunc: (fileStream: plugins.stream.Readable) => Promise<void>
): Promise<void> => {
const files = plugins.fs.readdirSync(directoryPath, { withFileTypes: true });
for (const file of files) {
const fullPath = plugins.path.join(directoryPath, file.name);
if (file.isDirectory()) {
await processDirectory(fullPath, asyncFunc); // Recursively call processDirectory for directories
} else if (file.isFile()) {
await processFile(fullPath, asyncFunc); // Call async function with the file stream and wait for it
}
}
};
/**
* Checks if a file is ready to be streamed (exists and is not empty).
*/
export const isFileReadyForStreaming = async (filePathArg: string): Promise<boolean> => {
try {
const stats = await plugins.fs.promises.stat(filePathArg);
return stats.size > 0;
} catch (error) {
if (error.code === 'ENOENT') { // File does not exist
return false;
}
throw error; // Rethrow other unexpected errors
}
};
/**
* Waits for a file to be ready for streaming (exists and is not empty).
*/
export const waitForFileToBeReadyForStreaming = (filePathArg: string): Promise<void> => {
return new Promise((resolve, reject) => {
// Normalize and resolve the file path
const filePath = plugins.path.resolve(filePathArg);
// Function to check file stats
const checkFile = (resolve: () => void, reject: (reason: any) => void) => {
plugins.fs.stat(filePath, (err, stats) => {
if (err) {
if (err.code === 'ENOENT') {
// File not found, wait and try again
return;
}
// Some other error occurred
return reject(err);
}
if (stats.size > 0) {
// File exists and is not empty, resolve the promise
resolve();
}
});
};
// Set up file watcher
const watcher = plugins.fs.watch(filePath, { persistent: false }, (eventType) => {
if (eventType === 'change' || eventType === 'rename') {
checkFile(resolve, reject);
}
});
// Check file immediately in case it's already ready
checkFile(resolve, reject);
// Error handling
watcher.on('error', (error) => {
watcher.close();
reject(error);
});
});
};
export class SmartReadStream extends plugins.stream.Readable {
private watcher: plugins.fs.FSWatcher | null = null;
private lastReadSize: number = 0;
private endTimeout: NodeJS.Timeout | null = null;
private filePath: string;
private endDelay: number;
private reading: boolean = false;
constructor(filePath: string, endDelay = 60000, opts?: plugins.stream.ReadableOptions) {
super(opts);
this.filePath = filePath;
this.endDelay = endDelay;
}
private startWatching(): void {
this.watcher = plugins.fs.watch(this.filePath, (eventType) => {
if (eventType === 'change') {
this.resetEndTimeout();
}
});
this.watcher.on('error', (error) => {
this.cleanup();
this.emit('error', error);
});
}
private resetEndTimeout(): void {
if (this.endTimeout) clearTimeout(this.endTimeout);
this.endTimeout = setTimeout(() => this.checkForEnd(), this.endDelay);
}
private checkForEnd(): void {
plugins.fs.stat(this.filePath, (err, stats) => {
if (err) {
this.emit('error', err);
return;
}
if (this.lastReadSize === stats.size) {
this.push(null); // Signal the end of the stream
this.cleanup();
} else {
this.lastReadSize = stats.size;
this.resetEndTimeout();
if (!this.reading) {
// We only want to continue reading if we were previously waiting for more data
this.reading = true;
this._read(10000); // Try to read more data
}
}
});
}
private cleanup(): void {
if (this.endTimeout) clearTimeout(this.endTimeout);
if (this.watcher) this.watcher.close();
}
_read(size: number): void {
this.reading = true;
const chunkSize = Math.min(size, 16384); // Read in chunks of 16KB
const buffer = Buffer.alloc(chunkSize);
plugins.fs.open(this.filePath, 'r', (err, fd) => {
if (err) {
this.emit('error', err);
return;
}
plugins.fs.read(fd, buffer, 0, chunkSize, this.lastReadSize, (err, bytesRead, buffer) => {
if (err) {
this.emit('error', err);
return;
}
if (bytesRead > 0) {
this.lastReadSize += bytesRead;
this.push(buffer.slice(0, bytesRead)); // Push the data onto the stream
} else {
this.reading = false; // No more data to read for now
this.resetEndTimeout();
}
plugins.fs.close(fd, (err) => {
if (err) {
this.emit('error', err);
}
});
});
});
}
_destroy(error: Error | null, callback: (error: Error | null) => void): void {
this.cleanup();
callback(error);
}
}

View File

@ -1,11 +1,12 @@
import * as plugins from './smartfile.plugins.js';
import * as fsMod from './smartfile.fs.js';
import * as fsStreamMod from './smartfile.fsstream.js';
import * as interpreterMod from './smartfile.interpreter.js';
import * as memoryMod from './smartfile.memory.js';
import * as plugins from './plugins.js';
import * as fsMod from './fs.js';
import * as fsStreamMod from './fsstream.js';
import * as interpreterMod from './interpreter.js';
import * as memoryMod from './memory.js';
export * from './smartfile.classes.smartfile.js';
export * from './smartfile.classes.virtualdirectory.js';
export * from './classes.smartfile.js';
export * from './classes.streamfile.js';
export * from './classes.virtualdirectory.js';
export const fs = fsMod;
export const fsStream = fsStreamMod;

View File

@ -1,4 +1,4 @@
import * as plugins from './smartfile.plugins.js';
import * as plugins from './plugins.js';
export let filetype = (pathArg: string): string => {
const extName = plugins.path.extname(pathArg);

View File

@ -1,7 +1,8 @@
import * as plugins from './smartfile.plugins.js';
import { Smartfile } from './smartfile.classes.smartfile.js';
import * as smartfileFs from './smartfile.fs.js';
import * as interpreter from './smartfile.interpreter.js';
import * as plugins from './plugins.js';
import { SmartFile } from './classes.smartfile.js';
import * as smartfileFs from './fs.js';
import * as interpreter from './interpreter.js';
import type { StreamFile } from './classes.streamfile.js';
/**
* converts file to Object
@ -24,7 +25,7 @@ export interface IToFsOptions {
* @param fileBaseArg
*/
export let toFs = async (
fileContentArg: string | Buffer | Smartfile,
fileContentArg: string | Buffer | SmartFile | StreamFile,
filePathArg: string,
optionsArg: IToFsOptions = {}
) => {
@ -41,7 +42,7 @@ export let toFs = async (
let filePath: string = filePathArg;
// handle Smartfile
if (fileContentArg instanceof Smartfile) {
if (fileContentArg instanceof SmartFile) {
fileContent = fileContentArg.contentBuffer;
// handle options
if (optionsArg.respectRelative) {
@ -83,7 +84,7 @@ export const toFsSync = (fileArg: string, filePathArg: string) => {
plugins.fsExtra.writeFileSync(filePath, fileString, { encoding: 'utf8' });
};
export let smartfileArrayToFs = async (smartfileArrayArg: Smartfile[], dirArg: string) => {
export let smartfileArrayToFs = async (smartfileArrayArg: SmartFile[], dirArg: string) => {
await smartfileFs.ensureDir(dirArg);
for (const smartfile of smartfileArrayArg) {
await toFs(smartfile, dirArg, {

View File

@ -1,8 +1,10 @@
// node native scope
import * as fs from 'fs';
import * as fsPromises from 'fs/promises';
import * as path from 'path';
import * as stream from 'stream';
export { fs, path };
export { fs, fsPromises, path, stream };
// @pushrocks scope
import * as lik from '@push.rocks/lik';

View File

@ -1,14 +0,0 @@
/*
This file contains logic for streaming things from and to the filesystem
*/
import * as plugins from './smartfile.plugins.js';
export const createReadStream = (pathArg: string) => {
return plugins.fs.createReadStream(pathArg);
};
export const createWriteStream = (pathArg: string) => {
return plugins.fs.createWriteStream(pathArg);
};
export const streamDirectory = async (dirPathArg: string) => {};

View File

@ -3,9 +3,12 @@
"experimentalDecorators": true,
"useDefineForClassFields": false,
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "nodenext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"verbatimModuleSyntax": true,
}
"verbatimModuleSyntax": true
},
"exclude": [
"dist_*/**/*.d.ts"
]
}