feat: add filepicker package
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -14,6 +14,8 @@ codeqldb
|
|||||||
coverage
|
coverage
|
||||||
lerna-debug.log
|
lerna-debug.log
|
||||||
|
|
||||||
|
packages/**/dist
|
||||||
|
|
||||||
**/.pnp.*
|
**/.pnp.*
|
||||||
**/.yarn/*
|
**/.yarn/*
|
||||||
!.yarn/patches
|
!.yarn/patches
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/ts-node-npm-10.8.2-f3c0c9eaee-1eede939be.zip
vendored
Normal file
BIN
.yarn/cache/ts-node-npm-10.8.2-f3c0c9eaee-1eede939be.zip
vendored
Normal file
Binary file not shown.
1
packages/encryption/.gitignore
vendored
1
packages/encryption/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
dist
|
|
||||||
1
packages/features/.gitignore
vendored
1
packages/features/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
dist
|
|
||||||
3
packages/filepicker/.eslintignore
Normal file
3
packages/filepicker/.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
example
|
||||||
6
packages/filepicker/.eslintrc
Normal file
6
packages/filepicker/.eslintrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../.eslintrc",
|
||||||
|
"parserOptions": {
|
||||||
|
"project": "./linter.tsconfig.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
344
packages/filepicker/CHANGELOG.md
Normal file
344
packages/filepicker/CHANGELOG.md
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
# Change Log
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.16.25](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.24...@standardnotes/filepicker@1.16.25) (2022-07-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.24](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.23...@standardnotes/filepicker@1.16.24) (2022-07-04)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add missing reflect-metadata package to all packages ([ce3a5bb](https://github.com/standardnotes/snjs/commit/ce3a5bbf3f1d2276ac4abc3eec3c6a44c8c3ba9b))
|
||||||
|
|
||||||
|
## [1.16.23](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.22...@standardnotes/filepicker@1.16.23) (2022-06-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.22](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.21...@standardnotes/filepicker@1.16.22) (2022-06-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.21](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.20...@standardnotes/filepicker@1.16.21) (2022-06-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.20](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.19...@standardnotes/filepicker@1.16.20) (2022-06-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.19](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.18...@standardnotes/filepicker@1.16.19) (2022-06-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.18](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.17...@standardnotes/filepicker@1.16.18) (2022-06-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.17](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.16...@standardnotes/filepicker@1.16.17) (2022-06-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.16](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.15...@standardnotes/filepicker@1.16.16) (2022-06-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.15](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.14...@standardnotes/filepicker@1.16.15) (2022-06-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.14](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.13...@standardnotes/filepicker@1.16.14) (2022-06-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.13](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.12...@standardnotes/filepicker@1.16.13) (2022-06-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.12](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.11...@standardnotes/filepicker@1.16.12) (2022-06-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.11](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.10...@standardnotes/filepicker@1.16.11) (2022-06-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.10](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.9...@standardnotes/filepicker@1.16.10) (2022-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.9](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.8...@standardnotes/filepicker@1.16.9) (2022-06-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.8](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.7...@standardnotes/filepicker@1.16.8) (2022-06-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.7](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.6...@standardnotes/filepicker@1.16.7) (2022-06-02)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* remove isLast dep from ordered byte chunker ([3385581](https://github.com/standardnotes/snjs/commit/33855817d8d96d100b7d4f423f59f00c55834b6f))
|
||||||
|
|
||||||
|
## [1.16.6](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.5...@standardnotes/filepicker@1.16.6) (2022-06-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.5](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.4...@standardnotes/filepicker@1.16.5) (2022-05-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.4](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.3...@standardnotes/filepicker@1.16.4) (2022-05-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.3](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.2...@standardnotes/filepicker@1.16.3) (2022-05-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.2](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.1...@standardnotes/filepicker@1.16.2) (2022-05-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.16.1](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.16.0...@standardnotes/filepicker@1.16.1) (2022-05-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
# [1.16.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.15.0...@standardnotes/filepicker@1.16.0) (2022-05-23)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* encrypted file cache ([#747](https://github.com/standardnotes/snjs/issues/747)) ([5b156a5](https://github.com/standardnotes/snjs/commit/5b156a5b4ee3365dac8e02653df129584a9dd4ef))
|
||||||
|
|
||||||
|
# [1.15.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.13...@standardnotes/filepicker@1.15.0) (2022-05-22)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* optional files navigation ([#745](https://github.com/standardnotes/snjs/issues/745)) ([8512166](https://github.com/standardnotes/snjs/commit/851216615478b57b11a570173f94ee598bec31c0))
|
||||||
|
|
||||||
|
## [1.14.13](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.12...@standardnotes/filepicker@1.14.13) (2022-05-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.12](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.11...@standardnotes/filepicker@1.14.12) (2022-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.11](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.10...@standardnotes/filepicker@1.14.11) (2022-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.10](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.9...@standardnotes/filepicker@1.14.10) (2022-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.9](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.8...@standardnotes/filepicker@1.14.9) (2022-05-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.8](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.7...@standardnotes/filepicker@1.14.8) (2022-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.7](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.6...@standardnotes/filepicker@1.14.7) (2022-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.6](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.5...@standardnotes/filepicker@1.14.6) (2022-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.5](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.4...@standardnotes/filepicker@1.14.5) (2022-05-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.4](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.3...@standardnotes/filepicker@1.14.4) (2022-05-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.3](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.2...@standardnotes/filepicker@1.14.3) (2022-05-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.2](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.1...@standardnotes/filepicker@1.14.2) (2022-05-13)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.14.1](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.14.0...@standardnotes/filepicker@1.14.1) (2022-05-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
# [1.14.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.13.7...@standardnotes/filepicker@1.14.0) (2022-05-12)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* file desktop backups ([#731](https://github.com/standardnotes/snjs/issues/731)) ([0dbce7d](https://github.com/standardnotes/snjs/commit/0dbce7dc9712fde848445b951079c81479c8bc11))
|
||||||
|
|
||||||
|
## [1.13.7](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.13.6...@standardnotes/filepicker@1.13.7) (2022-05-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.13.6](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.13.4...@standardnotes/filepicker@1.13.6) (2022-05-04)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.13.5](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.13.4...@standardnotes/filepicker@1.13.5) (2022-05-04)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.13.4](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.13.3...@standardnotes/filepicker@1.13.4) (2022-04-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.13.3](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.13.2...@standardnotes/filepicker@1.13.3) (2022-04-26)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* file size calculation to binary ([#709](https://github.com/standardnotes/snjs/issues/709)) ([5773bc7](https://github.com/standardnotes/snjs/commit/5773bc7a2d5f2d79b9d9633fafa660c4b13b42e0))
|
||||||
|
|
||||||
|
## [1.13.2](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.13.1...@standardnotes/filepicker@1.13.2) (2022-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.13.1](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.13.0...@standardnotes/filepicker@1.13.1) (2022-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
# [1.13.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.12.0...@standardnotes/filepicker@1.13.0) (2022-04-22)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* in memory file cache ([#705](https://github.com/standardnotes/snjs/issues/705)) ([fca294a](https://github.com/standardnotes/snjs/commit/fca294a84256e03272e3b1b29b3dc478cddf9c28))
|
||||||
|
|
||||||
|
# [1.12.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.11.0...@standardnotes/filepicker@1.12.0) (2022-04-21)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add GB support to formatSizeAsReadableString ([#701](https://github.com/standardnotes/snjs/issues/701)) ([bafd52a](https://github.com/standardnotes/snjs/commit/bafd52a8e4d51229e37ec3f8bb6ea01cf2b7e584))
|
||||||
|
|
||||||
|
# [1.11.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.10.6...@standardnotes/filepicker@1.11.0) (2022-04-15)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* no merge payloads in payload manager ([#693](https://github.com/standardnotes/snjs/issues/693)) ([68a577c](https://github.com/standardnotes/snjs/commit/68a577cb887fd2d5556dc9ddec461f6ae665fcb6))
|
||||||
|
|
||||||
|
## [1.10.6](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.10.5...@standardnotes/filepicker@1.10.6) (2022-04-12)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* export byte_chunker from filepicker package ([#689](https://github.com/standardnotes/snjs/issues/689)) ([2541250](https://github.com/standardnotes/snjs/commit/2541250d7c01a0763c3162e7e68b28cb4c075322))
|
||||||
|
|
||||||
|
## [1.10.5](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.10.4...@standardnotes/filepicker@1.10.5) (2022-04-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.10.4](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.10.3...@standardnotes/filepicker@1.10.4) (2022-03-31)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
## [1.10.3](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.10.2...@standardnotes/filepicker@1.10.3) (2022-03-24)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* streaming reader error on abort selection ([#674](https://github.com/standardnotes/snjs/issues/674)) ([8c36554](https://github.com/standardnotes/snjs/commit/8c36554a95117ed6e42c75d0dc29e01cb8de7a54))
|
||||||
|
|
||||||
|
## [1.10.2](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.10.1...@standardnotes/filepicker@1.10.2) (2022-03-23)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* class file reader chunking ([63a8494](https://github.com/standardnotes/snjs/commit/63a84945f2935c2c2a23b1aa4ea26dcfd24f08d4))
|
||||||
|
|
||||||
|
## [1.10.1](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.10.0...@standardnotes/filepicker@1.10.1) (2022-03-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
|
|
||||||
|
# [1.10.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.9.0...@standardnotes/filepicker@1.10.0) (2022-03-11)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* remove ext property from files in favor of mimetype ([#650](https://github.com/standardnotes/snjs/issues/650)) ([d2e7e23](https://github.com/standardnotes/snjs/commit/d2e7e23ec117c505f2f38b9edea539ad3a6d70e2))
|
||||||
|
|
||||||
|
# [1.9.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.8.0...@standardnotes/filepicker@1.9.0) (2022-03-10)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* store file mimeType along with name & ext ([#648](https://github.com/standardnotes/snjs/issues/648)) ([05bf273](https://github.com/standardnotes/snjs/commit/05bf2737282f2d068e354c4d05fbe5390a19e613))
|
||||||
|
|
||||||
|
# [1.8.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.7.0...@standardnotes/filepicker@1.8.0) (2022-03-10)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **filepicker:** multiple file selection ([#644](https://github.com/standardnotes/snjs/issues/644)) ([1bcdaf4](https://github.com/standardnotes/snjs/commit/1bcdaf4d2e05e1280ba8646683be71eebf95ee2d))
|
||||||
|
|
||||||
|
# [1.7.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.6.0...@standardnotes/filepicker@1.7.0) (2022-03-09)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* export file operation type ([#643](https://github.com/standardnotes/snjs/issues/643)) ([ff5f136](https://github.com/standardnotes/snjs/commit/ff5f136655a8089a47c7eaa04e1e13e58852c93f))
|
||||||
|
|
||||||
|
# [1.6.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.5.0...@standardnotes/filepicker@1.6.0) (2022-03-09)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* allow passing file or filehandle to reader as param ([#641](https://github.com/standardnotes/snjs/issues/641)) ([48b63a2](https://github.com/standardnotes/snjs/commit/48b63a270d647dd864edbcc8316146b4a32a634e))
|
||||||
|
|
||||||
|
# [1.5.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.4.0...@standardnotes/filepicker@1.5.0) (2022-03-09)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add formatSizeToReadableString function ([#635](https://github.com/standardnotes/snjs/issues/635)) ([8688783](https://github.com/standardnotes/snjs/commit/8688783ac95073631e752cdb76011bca75a29794))
|
||||||
|
|
||||||
|
# [1.4.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.3.0...@standardnotes/filepicker@1.4.0) (2022-03-08)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* determine files host dynamically ([#637](https://github.com/standardnotes/snjs/issues/637)) ([8ae8d32](https://github.com/standardnotes/snjs/commit/8ae8d32a2469cc6b5b42bfc68ec63200d6bc49ed))
|
||||||
|
|
||||||
|
# [1.3.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.2.1...@standardnotes/filepicker@1.3.0) (2022-03-07)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add renameFile function to itemManager ([#633](https://github.com/standardnotes/snjs/issues/633)) ([828f0d8](https://github.com/standardnotes/snjs/commit/828f0d8c79736b2ede1dd244e1e59569a88e6440))
|
||||||
|
|
||||||
|
## [1.2.1](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.2.0...@standardnotes/filepicker@1.2.1) (2022-03-03)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* store selected file variable ([52e9c49](https://github.com/standardnotes/snjs/commit/52e9c494868c809f9cc894c182056c92e2f23133))
|
||||||
|
|
||||||
|
# [1.2.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.1.3...@standardnotes/filepicker@1.2.0) (2022-03-03)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* split file picker selection and reading in two ([d5e98a1](https://github.com/standardnotes/snjs/commit/d5e98a15213c9976b629fe401d8ba5f31379f391))
|
||||||
|
|
||||||
|
## [1.1.3](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.1.1...@standardnotes/filepicker@1.1.3) (2022-02-28)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add pseudo change to get lerna to trigger ([41e6817](https://github.com/standardnotes/snjs/commit/41e6817bbf726b0932cdf16f58622328b9e42803))
|
||||||
|
|
||||||
|
## [1.1.2](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.1.1...@standardnotes/filepicker@1.1.2) (2022-02-28)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add pseudo change to get lerna to trigger ([41e6817](https://github.com/standardnotes/snjs/commit/41e6817bbf726b0932cdf16f58622328b9e42803))
|
||||||
|
|
||||||
|
## [1.1.1](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.1.0...@standardnotes/filepicker@1.1.1) (2022-02-25)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* variable reference ([960fce3](https://github.com/standardnotes/snjs/commit/960fce3d56f4a9204c373253cb75874766e6cf85))
|
||||||
|
|
||||||
|
# [1.1.0](https://github.com/standardnotes/snjs/compare/@standardnotes/filepicker@1.0.1...@standardnotes/filepicker@1.1.0) (2022-02-25)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* files improvements ([#612](https://github.com/standardnotes/snjs/issues/612)) ([27a29a9](https://github.com/standardnotes/snjs/commit/27a29a98fdf966ddcbe93df951db1358848f6aab))
|
||||||
|
|
||||||
|
## 1.0.1 (2022-02-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/filepicker
|
||||||
5
packages/filepicker/example/.eslintrc
Normal file
5
packages/filepicker/example/.eslintrc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"rules": {
|
||||||
|
"no-console": ["off"]
|
||||||
|
}
|
||||||
|
}
|
||||||
21
packages/filepicker/example/index.html
Normal file
21
packages/filepicker/example/index.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<label>Classic File Picker</label>
|
||||||
|
<button id="filePicker">Classic Picker</button>
|
||||||
|
|
||||||
|
<label>FileSystem API Picker</label>
|
||||||
|
<button id="fileSystemUploadButton">FileSystem Upload File</button>
|
||||||
|
<button id="downloadButton">Download File</button>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
42
packages/filepicker/example/package.json
Normal file
42
packages/filepicker/example/package.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "files-demo",
|
||||||
|
"main": "dist/src/index.js",
|
||||||
|
"types": "dist/src/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist/src"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rm -fr dist",
|
||||||
|
"prestart": "yarn clean",
|
||||||
|
"start": "webpack-dev-server --config webpack.config.js",
|
||||||
|
"watch": "webpack -w --config webpack.config.js",
|
||||||
|
"prebuild": "yarn clean",
|
||||||
|
"build": "tsc -p tsconfig.json",
|
||||||
|
"lint": "eslint . --ext .ts"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.15.8",
|
||||||
|
"@babel/preset-env": "^7.15.8",
|
||||||
|
"@babel/preset-typescript": "^7.15.0",
|
||||||
|
"@standardnotes/config": "^2.2.0",
|
||||||
|
"@types/wicg-native-file-system": "^2020.6.0",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"babel-loader": "^8.2.3",
|
||||||
|
"html-webpack-plugin": "^5.5.0",
|
||||||
|
"ts-loader": "^9.2.6",
|
||||||
|
"typescript": "^4.0.5",
|
||||||
|
"typescript-eslint": "0.0.1-alpha.0",
|
||||||
|
"webpack": "^5.59.1",
|
||||||
|
"webpack-cli": "^4.9.1",
|
||||||
|
"webpack-dev-server": "^4.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@standardnotes/sncrypto-web": "^1.7.0",
|
||||||
|
"@standardnotes/snjs": "^2.61.3",
|
||||||
|
"regenerator-runtime": "^0.13.9"
|
||||||
|
}
|
||||||
|
}
|
||||||
56
packages/filepicker/example/src/classic_file_api.ts
Normal file
56
packages/filepicker/example/src/classic_file_api.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { SNApplication, ContentType, FileItem, ClientDisplayableError } from '../../../snjs'
|
||||||
|
import { ClassicFileReader, ClassicFileSaver } from '../../../filepicker'
|
||||||
|
|
||||||
|
export class ClassicFileApi {
|
||||||
|
constructor(private application: SNApplication) {
|
||||||
|
this.configureFilePicker()
|
||||||
|
}
|
||||||
|
|
||||||
|
configureFilePicker(): void {
|
||||||
|
const input = document.getElementById('filePicker') as HTMLInputElement
|
||||||
|
input.onclick = () => {
|
||||||
|
void this.openFilePicker()
|
||||||
|
}
|
||||||
|
console.log('Classic file picker ready.')
|
||||||
|
}
|
||||||
|
|
||||||
|
async openFilePicker(): Promise<void> {
|
||||||
|
const files = await ClassicFileReader.selectFiles()
|
||||||
|
for (const file of files) {
|
||||||
|
const operation = await this.application.files.beginNewFileUpload()
|
||||||
|
if (operation instanceof ClientDisplayableError) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const fileResult = await ClassicFileReader.readFile(file, 2_000_000, async (chunk, index, isLast) => {
|
||||||
|
await this.application.files.pushBytesForUpload(operation, chunk, index, isLast)
|
||||||
|
})
|
||||||
|
const snFile = await this.application.files.finishUpload(operation, fileResult)
|
||||||
|
|
||||||
|
if (snFile instanceof ClientDisplayableError) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const bytes = await this.downloadFileBytes(snFile.remoteIdentifier)
|
||||||
|
|
||||||
|
new ClassicFileSaver().saveFile(`${snFile.name}.${snFile.ext}`, bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadFileBytes = async (remoteIdentifier: string): Promise<Uint8Array> => {
|
||||||
|
console.log('Downloading file', remoteIdentifier)
|
||||||
|
const file = this.application['itemManager']
|
||||||
|
.getItems(ContentType.File)
|
||||||
|
.find((file: FileItem) => file.remoteIdentifier === remoteIdentifier)
|
||||||
|
|
||||||
|
let receivedBytes = new Uint8Array()
|
||||||
|
|
||||||
|
await this.application.files.downloadFile(file, async (decryptedBytes: Uint8Array) => {
|
||||||
|
console.log(`Downloaded ${decryptedBytes.length} bytes`)
|
||||||
|
receivedBytes = new Uint8Array([...receivedBytes, ...decryptedBytes])
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Successfully downloaded and decrypted file!')
|
||||||
|
|
||||||
|
return receivedBytes
|
||||||
|
}
|
||||||
|
}
|
||||||
68
packages/filepicker/example/src/file_system_api.ts
Normal file
68
packages/filepicker/example/src/file_system_api.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { StreamingFileReader, StreamingFileSaver } from '../../../filepicker'
|
||||||
|
import { SNApplication, FileItem, ClientDisplayableError } from '../../../snjs'
|
||||||
|
|
||||||
|
export class FileSystemApi {
|
||||||
|
private uploadedFiles: FileItem[] = []
|
||||||
|
|
||||||
|
constructor(private application: SNApplication) {
|
||||||
|
this.configureFilePicker()
|
||||||
|
this.configureDownloadButton()
|
||||||
|
}
|
||||||
|
|
||||||
|
get downloadButton(): HTMLButtonElement {
|
||||||
|
return document.getElementById('downloadButton') as HTMLButtonElement
|
||||||
|
}
|
||||||
|
|
||||||
|
configureDownloadButton(): void {
|
||||||
|
this.downloadButton.onclick = this.downloadFiles
|
||||||
|
this.downloadButton.style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
configureFilePicker(): void {
|
||||||
|
const button = document.getElementById('fileSystemUploadButton') as HTMLButtonElement
|
||||||
|
button.onclick = this.uploadFiles
|
||||||
|
console.log('File picker ready.')
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadFiles = async (): Promise<void> => {
|
||||||
|
const snFiles = []
|
||||||
|
const selectedFiles = await StreamingFileReader.selectFiles()
|
||||||
|
for (const file of selectedFiles) {
|
||||||
|
const operation = await this.application.files.beginNewFileUpload()
|
||||||
|
if (operation instanceof ClientDisplayableError) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const fileResult = await StreamingFileReader.readFile(file, 2_000_000, async (chunk, index, isLast) => {
|
||||||
|
await this.application.files.pushBytesForUpload(operation, chunk, index, isLast)
|
||||||
|
})
|
||||||
|
|
||||||
|
const snFile = await this.application.files.finishUpload(operation, fileResult)
|
||||||
|
|
||||||
|
snFiles.push(snFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.downloadButton.style.display = ''
|
||||||
|
|
||||||
|
this.uploadedFiles = snFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadFiles = async (): Promise<void> => {
|
||||||
|
for (const snFile of this.uploadedFiles) {
|
||||||
|
console.log('Downloading file', snFile.remoteIdentifier)
|
||||||
|
|
||||||
|
const saver = new StreamingFileSaver(snFile.name)
|
||||||
|
await saver.selectFileToSaveTo()
|
||||||
|
saver.loggingEnabled = true
|
||||||
|
|
||||||
|
await this.application.files.downloadFile(snFile, async (decryptedBytes: Uint8Array) => {
|
||||||
|
console.log(`Pushing ${decryptedBytes.length} decrypted bytes to disk`)
|
||||||
|
await saver.pushBytes(decryptedBytes)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Closing file saver reader')
|
||||||
|
await saver.finish()
|
||||||
|
|
||||||
|
console.log('Successfully downloaded and decrypted file!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
packages/filepicker/example/src/index.ts
Normal file
89
packages/filepicker/example/src/index.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { SNApplication, Environment, Platform, SNLog } from '../../../snjs'
|
||||||
|
import WebDeviceInterface from './web_device_interface'
|
||||||
|
import { SNWebCrypto } from '../../../sncrypto-web'
|
||||||
|
import { ClassicFileApi } from './classic_file_api'
|
||||||
|
import { FileSystemApi } from './file_system_api'
|
||||||
|
|
||||||
|
SNLog.onLog = console.log
|
||||||
|
SNLog.onError = console.error
|
||||||
|
|
||||||
|
console.log('Clearing localStorage...')
|
||||||
|
localStorage.clear()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Important:
|
||||||
|
* If reusing e2e docker servers, you must edit docker/auth.env ACCESS_TOKEN_AGE
|
||||||
|
* and REFRESH_TOKEN_AGE and increase their ttl.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const host = 'http://localhost:3123'
|
||||||
|
const mocksHost = 'http://localhost:3124'
|
||||||
|
|
||||||
|
const application = new SNApplication({
|
||||||
|
environment: Environment.Web,
|
||||||
|
platform: Platform.MacWeb,
|
||||||
|
deviceInterface: new WebDeviceInterface(),
|
||||||
|
crypto: new SNWebCrypto(),
|
||||||
|
alertService: {
|
||||||
|
confirm: async () => true,
|
||||||
|
alert: async () => {
|
||||||
|
alert()
|
||||||
|
},
|
||||||
|
blockingDialog: () => () => {
|
||||||
|
confirm()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
identifier: `${Math.random()}`,
|
||||||
|
defaultHost: host,
|
||||||
|
appVersion: '1.0.0',
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Created application', application)
|
||||||
|
|
||||||
|
export async function publishMockedEvent(eventType: string, eventPayload: unknown): Promise<void> {
|
||||||
|
await fetch(`${mocksHost}/events`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
eventType,
|
||||||
|
eventPayload,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const run = async () => {
|
||||||
|
console.log('Preparing for launch...')
|
||||||
|
await application.prepareForLaunch({
|
||||||
|
receiveChallenge: () => {
|
||||||
|
console.warn('Ignoring challenge')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await application.launch()
|
||||||
|
console.log('Application launched...')
|
||||||
|
|
||||||
|
const email = String(Math.random())
|
||||||
|
const password = String(Math.random())
|
||||||
|
|
||||||
|
console.log('Registering account...')
|
||||||
|
await application.register(email, password)
|
||||||
|
console.log(`Registered account ${email}/${password}. Be sure to edit docker/auth.env to increase session TTL.`)
|
||||||
|
|
||||||
|
console.log('Creating mock subscription...')
|
||||||
|
await publishMockedEvent('SUBSCRIPTION_PURCHASED', {
|
||||||
|
userEmail: email,
|
||||||
|
subscriptionId: 1,
|
||||||
|
subscriptionName: 'PLUS_PLAN',
|
||||||
|
subscriptionExpiresAt: (new Date().getTime() + 3_600_000) * 1_000,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
offline: false,
|
||||||
|
})
|
||||||
|
console.log('Successfully created mock subscription...')
|
||||||
|
|
||||||
|
new ClassicFileApi(application)
|
||||||
|
new FileSystemApi(application)
|
||||||
|
}
|
||||||
|
|
||||||
|
void run()
|
||||||
138
packages/filepicker/example/src/web_device_interface.js
Normal file
138
packages/filepicker/example/src/web_device_interface.js
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/* eslint-disable no-undef */
|
||||||
|
const KEYCHAIN_STORAGE_KEY = 'keychain'
|
||||||
|
|
||||||
|
export default class WebDeviceInterface {
|
||||||
|
async getRawStorageValue(key) {
|
||||||
|
return localStorage.getItem(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getJsonParsedRawStorageValue(key) {
|
||||||
|
const value = await this.getRawStorageValue(key)
|
||||||
|
if (isNullOrUndefined(value)) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch (e) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllRawStorageKeyValues() {
|
||||||
|
const results = []
|
||||||
|
for (const key of Object.keys(localStorage)) {
|
||||||
|
results.push({
|
||||||
|
key: key,
|
||||||
|
value: localStorage[key],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
async setRawStorageValue(key, value) {
|
||||||
|
localStorage.setItem(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeRawStorageValue(key) {
|
||||||
|
localStorage.removeItem(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeAllRawStorageValues() {
|
||||||
|
localStorage.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
async openDatabase(_identifier) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
_getDatabaseKeyPrefix(identifier) {
|
||||||
|
if (identifier) {
|
||||||
|
return `${identifier}-item-`
|
||||||
|
} else {
|
||||||
|
return 'item-'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_keyForPayloadId(id, identifier) {
|
||||||
|
return `${this._getDatabaseKeyPrefix(identifier)}${id}`
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllRawDatabasePayloads(identifier) {
|
||||||
|
const models = []
|
||||||
|
for (const key in localStorage) {
|
||||||
|
if (key.startsWith(this._getDatabaseKeyPrefix(identifier))) {
|
||||||
|
models.push(JSON.parse(localStorage[key]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return models
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveRawDatabasePayload(payload, identifier) {
|
||||||
|
localStorage.setItem(this._keyForPayloadId(payload.uuid, identifier), JSON.stringify(payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveRawDatabasePayloads(payloads, identifier) {
|
||||||
|
for (const payload of payloads) {
|
||||||
|
await this.saveRawDatabasePayload(payload, identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeRawDatabasePayloadWithId(id, identifier) {
|
||||||
|
localStorage.removeItem(this._keyForPayloadId(id, identifier))
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeAllRawDatabasePayloads(identifier) {
|
||||||
|
for (const key in localStorage) {
|
||||||
|
if (key.startsWith(this._getDatabaseKeyPrefix(identifier))) {
|
||||||
|
delete localStorage[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @keychain */
|
||||||
|
async getNamespacedKeychainValue(identifier) {
|
||||||
|
const keychain = await this.getRawKeychainValue(identifier)
|
||||||
|
if (!keychain) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return keychain[identifier]
|
||||||
|
}
|
||||||
|
|
||||||
|
async setNamespacedKeychainValue(value, identifier) {
|
||||||
|
let keychain = await this.getRawKeychainValue()
|
||||||
|
if (!keychain) {
|
||||||
|
keychain = {}
|
||||||
|
}
|
||||||
|
localStorage.setItem(
|
||||||
|
KEYCHAIN_STORAGE_KEY,
|
||||||
|
JSON.stringify({
|
||||||
|
...keychain,
|
||||||
|
[identifier]: value,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearNamespacedKeychainValue(identifier) {
|
||||||
|
const keychain = await this.getRawKeychainValue()
|
||||||
|
if (!keychain) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete keychain[identifier]
|
||||||
|
localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(keychain))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Allows unit tests to set legacy keychain structure as it was <= 003 */
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
async setLegacyRawKeychainValue(value) {
|
||||||
|
localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRawKeychainValue() {
|
||||||
|
const keychain = localStorage.getItem(KEYCHAIN_STORAGE_KEY)
|
||||||
|
return JSON.parse(keychain)
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearRawKeychainValue() {
|
||||||
|
localStorage.removeItem(KEYCHAIN_STORAGE_KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
packages/filepicker/example/tsconfig.json
Normal file
11
packages/filepicker/example/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "dist",
|
||||||
|
"target": "es2015",
|
||||||
|
"module": "es2015",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"baseUrl": ".",
|
||||||
|
},
|
||||||
|
"exclude": ["dist", "node_modules"]
|
||||||
|
}
|
||||||
54
packages/filepicker/example/webpack.config.js
Normal file
54
packages/filepicker/example/webpack.config.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = (env) => {
|
||||||
|
return {
|
||||||
|
entry: './src/index.ts',
|
||||||
|
output: {
|
||||||
|
filename: './dist/index.js',
|
||||||
|
},
|
||||||
|
mode: 'development',
|
||||||
|
optimization: {
|
||||||
|
minimize: false,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: './index.html',
|
||||||
|
inject: true,
|
||||||
|
templateParameters: {
|
||||||
|
env: process.env,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
devServer: {
|
||||||
|
hot: 'only',
|
||||||
|
static: './public',
|
||||||
|
port: 3030,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.tsx', '.js'],
|
||||||
|
fallback: {
|
||||||
|
crypto: false,
|
||||||
|
path: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(js|tsx?)$/,
|
||||||
|
exclude: /(node_modules)/,
|
||||||
|
use: [
|
||||||
|
'babel-loader',
|
||||||
|
{
|
||||||
|
loader: 'ts-loader',
|
||||||
|
options: {
|
||||||
|
transpileOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
4100
packages/filepicker/example/yarn.lock
Normal file
4100
packages/filepicker/example/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
14
packages/filepicker/jest.config.js
Normal file
14
packages/filepicker/jest.config.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const base = require('../../node_modules/@standardnotes/config/src/jest.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...base,
|
||||||
|
globals: {
|
||||||
|
'ts-jest': {
|
||||||
|
tsconfig: 'tsconfig.json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
coveragePathIgnorePatterns: [
|
||||||
|
"/example/"
|
||||||
|
]
|
||||||
|
};
|
||||||
4
packages/filepicker/linter.tsconfig.json
Normal file
4
packages/filepicker/linter.tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": ["dist", "example"]
|
||||||
|
}
|
||||||
42
packages/filepicker/package.json
Normal file
42
packages/filepicker/package.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "@standardnotes/filepicker",
|
||||||
|
"version": "1.17.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0 <17.0.0"
|
||||||
|
},
|
||||||
|
"description": "Web filepicker for Standard Notes projects",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"author": "Standard Notes",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rm -fr dist",
|
||||||
|
"prestart": "yarn clean",
|
||||||
|
"start": "tsc -p tsconfig.json --watch",
|
||||||
|
"prebuild": "yarn clean",
|
||||||
|
"build": "tsc -p tsconfig.json",
|
||||||
|
"lint": "eslint . --ext .ts",
|
||||||
|
"test:unit": "jest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^27.4.1",
|
||||||
|
"@types/wicg-file-system-access": "^2020.9.5",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
||||||
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"jest": "^27.5.1",
|
||||||
|
"ts-jest": "^27.1.3",
|
||||||
|
"ts-node": "^10.5.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@standardnotes/common": "^1.23.1",
|
||||||
|
"@standardnotes/services": "^1.13.23",
|
||||||
|
"@standardnotes/utils": "^1.6.12",
|
||||||
|
"reflect-metadata": "^0.1.13"
|
||||||
|
}
|
||||||
|
}
|
||||||
78
packages/filepicker/src/Cache/FileMemoryCache.spec.ts
Normal file
78
packages/filepicker/src/Cache/FileMemoryCache.spec.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { EncryptedBytes } from './../TypedBytes'
|
||||||
|
import { FileMemoryCache } from './FileMemoryCache'
|
||||||
|
|
||||||
|
describe('file memory cache', () => {
|
||||||
|
const createBytes = (size: number): EncryptedBytes => {
|
||||||
|
return { encryptedBytes: new TextEncoder().encode('a'.repeat(size)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should add file', () => {
|
||||||
|
const cache = new FileMemoryCache(5)
|
||||||
|
const file = createBytes(1)
|
||||||
|
cache.add('123', file)
|
||||||
|
|
||||||
|
expect(cache.get('123')).toEqual(file)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should fail to add file if exceeds maximum', () => {
|
||||||
|
const maxSize = 5
|
||||||
|
const cache = new FileMemoryCache(maxSize)
|
||||||
|
const file = createBytes(maxSize + 1)
|
||||||
|
|
||||||
|
expect(cache.add('123', file)).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should allow filling files up to limit', () => {
|
||||||
|
const cache = new FileMemoryCache(5)
|
||||||
|
|
||||||
|
cache.add('1', createBytes(3))
|
||||||
|
cache.add('2', createBytes(2))
|
||||||
|
|
||||||
|
expect(cache.get('1')).toBeTruthy()
|
||||||
|
expect(cache.get('2')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should clear early files when adding new files above limit', () => {
|
||||||
|
const cache = new FileMemoryCache(5)
|
||||||
|
|
||||||
|
cache.add('1', createBytes(3))
|
||||||
|
cache.add('2', createBytes(2))
|
||||||
|
cache.add('3', createBytes(5))
|
||||||
|
|
||||||
|
expect(cache.get('1')).toBeFalsy()
|
||||||
|
expect(cache.get('2')).toBeFalsy()
|
||||||
|
expect(cache.get('3')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should remove single file', () => {
|
||||||
|
const cache = new FileMemoryCache(5)
|
||||||
|
|
||||||
|
cache.add('1', createBytes(3))
|
||||||
|
cache.add('2', createBytes(2))
|
||||||
|
|
||||||
|
cache.remove('1')
|
||||||
|
|
||||||
|
expect(cache.get('1')).toBeFalsy()
|
||||||
|
expect(cache.get('2')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should clear all files', () => {
|
||||||
|
const cache = new FileMemoryCache(5)
|
||||||
|
|
||||||
|
cache.add('1', createBytes(3))
|
||||||
|
cache.add('2', createBytes(2))
|
||||||
|
cache.clear()
|
||||||
|
|
||||||
|
expect(cache.get('1')).toBeFalsy()
|
||||||
|
expect(cache.get('2')).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return correct size', () => {
|
||||||
|
const cache = new FileMemoryCache(20)
|
||||||
|
|
||||||
|
cache.add('1', createBytes(3))
|
||||||
|
cache.add('2', createBytes(10))
|
||||||
|
|
||||||
|
expect(cache.size).toEqual(13)
|
||||||
|
})
|
||||||
|
})
|
||||||
48
packages/filepicker/src/Cache/FileMemoryCache.ts
Normal file
48
packages/filepicker/src/Cache/FileMemoryCache.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { removeFromArray } from '@standardnotes/utils'
|
||||||
|
import { Uuid } from '@standardnotes/common'
|
||||||
|
import { EncryptedBytes } from '../TypedBytes'
|
||||||
|
|
||||||
|
export class FileMemoryCache {
|
||||||
|
private cache: Record<Uuid, EncryptedBytes> = {}
|
||||||
|
private orderedQueue: Uuid[] = []
|
||||||
|
|
||||||
|
constructor(public readonly maxSize: number) {}
|
||||||
|
|
||||||
|
add(uuid: Uuid, data: EncryptedBytes): boolean {
|
||||||
|
if (data.encryptedBytes.length > this.maxSize) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this.size + data.encryptedBytes.length > this.maxSize) {
|
||||||
|
this.remove(this.orderedQueue[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cache[uuid] = data
|
||||||
|
|
||||||
|
this.orderedQueue.push(uuid)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
get size(): number {
|
||||||
|
return Object.values(this.cache)
|
||||||
|
.map((bytes) => bytes.encryptedBytes.length)
|
||||||
|
.reduce((total, fileLength) => total + fileLength, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
get(uuid: Uuid): EncryptedBytes | undefined {
|
||||||
|
return this.cache[uuid]
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(uuid: Uuid): void {
|
||||||
|
delete this.cache[uuid]
|
||||||
|
|
||||||
|
removeFromArray(this.orderedQueue, uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
this.cache = {}
|
||||||
|
|
||||||
|
this.orderedQueue = []
|
||||||
|
}
|
||||||
|
}
|
||||||
70
packages/filepicker/src/Chunker/ByteChunker.spec.ts
Normal file
70
packages/filepicker/src/Chunker/ByteChunker.spec.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { ByteChunker } from './ByteChunker'
|
||||||
|
|
||||||
|
const chunkOfSize = (size: number) => {
|
||||||
|
return new TextEncoder().encode('a'.repeat(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('byte chunker', () => {
|
||||||
|
it('should hold back small chunks until minimum size is met', async () => {
|
||||||
|
let receivedBytes = new Uint8Array()
|
||||||
|
let numChunks = 0
|
||||||
|
const chunker = new ByteChunker(100, async (bytes) => {
|
||||||
|
numChunks++
|
||||||
|
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
|
||||||
|
})
|
||||||
|
|
||||||
|
await chunker.addBytes(chunkOfSize(50), false)
|
||||||
|
await chunker.addBytes(chunkOfSize(50), false)
|
||||||
|
await chunker.addBytes(chunkOfSize(50), false)
|
||||||
|
await chunker.addBytes(chunkOfSize(50), true)
|
||||||
|
|
||||||
|
expect(numChunks).toEqual(2)
|
||||||
|
expect(receivedBytes.length).toEqual(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should send back big chunks immediately', async () => {
|
||||||
|
let receivedBytes = new Uint8Array()
|
||||||
|
let numChunks = 0
|
||||||
|
const chunker = new ByteChunker(100, async (bytes) => {
|
||||||
|
numChunks++
|
||||||
|
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
|
||||||
|
})
|
||||||
|
|
||||||
|
await chunker.addBytes(chunkOfSize(150), false)
|
||||||
|
await chunker.addBytes(chunkOfSize(150), false)
|
||||||
|
await chunker.addBytes(chunkOfSize(150), false)
|
||||||
|
await chunker.addBytes(chunkOfSize(50), true)
|
||||||
|
|
||||||
|
expect(numChunks).toEqual(4)
|
||||||
|
expect(receivedBytes.length).toEqual(500)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('last chunk should be popped regardless of size', async () => {
|
||||||
|
let receivedBytes = new Uint8Array()
|
||||||
|
let numChunks = 0
|
||||||
|
const chunker = new ByteChunker(100, async (bytes) => {
|
||||||
|
numChunks++
|
||||||
|
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
|
||||||
|
})
|
||||||
|
|
||||||
|
await chunker.addBytes(chunkOfSize(50), false)
|
||||||
|
await chunker.addBytes(chunkOfSize(25), true)
|
||||||
|
|
||||||
|
expect(numChunks).toEqual(1)
|
||||||
|
expect(receivedBytes.length).toEqual(75)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('single chunk should be popped immediately', async () => {
|
||||||
|
let receivedBytes = new Uint8Array()
|
||||||
|
let numChunks = 0
|
||||||
|
const chunker = new ByteChunker(100, async (bytes) => {
|
||||||
|
numChunks++
|
||||||
|
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
|
||||||
|
})
|
||||||
|
|
||||||
|
await chunker.addBytes(chunkOfSize(50), true)
|
||||||
|
|
||||||
|
expect(numChunks).toEqual(1)
|
||||||
|
expect(receivedBytes.length).toEqual(50)
|
||||||
|
})
|
||||||
|
})
|
||||||
35
packages/filepicker/src/Chunker/ByteChunker.ts
Normal file
35
packages/filepicker/src/Chunker/ByteChunker.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { OnChunkCallback } from '../types'
|
||||||
|
|
||||||
|
export class ByteChunker {
|
||||||
|
public loggingEnabled = false
|
||||||
|
private bytes = new Uint8Array()
|
||||||
|
private index = 1
|
||||||
|
|
||||||
|
constructor(private minimumChunkSize: number, private onChunk: OnChunkCallback) {}
|
||||||
|
|
||||||
|
private log(...args: any[]): void {
|
||||||
|
if (!this.loggingEnabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async addBytes(bytes: Uint8Array, isLast: boolean): Promise<void> {
|
||||||
|
this.bytes = new Uint8Array([...this.bytes, ...bytes])
|
||||||
|
|
||||||
|
this.log(`Chunker adding ${bytes.length}, total size ${this.bytes.length}`)
|
||||||
|
|
||||||
|
if (this.bytes.length >= this.minimumChunkSize || isLast) {
|
||||||
|
await this.popBytes(isLast)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async popBytes(isLast: boolean): Promise<void> {
|
||||||
|
const maxIndex = Math.max(this.minimumChunkSize, this.bytes.length)
|
||||||
|
const chunk = this.bytes.slice(0, maxIndex)
|
||||||
|
this.bytes = new Uint8Array([...this.bytes.slice(maxIndex)])
|
||||||
|
this.log(`Chunker popping ${chunk.length}, total size in queue ${this.bytes.length}`)
|
||||||
|
await this.onChunk(chunk, this.index++, isLast)
|
||||||
|
}
|
||||||
|
}
|
||||||
23
packages/filepicker/src/Chunker/OrderedByteChunker.spec.ts
Normal file
23
packages/filepicker/src/Chunker/OrderedByteChunker.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { OrderedByteChunker } from './OrderedByteChunker'
|
||||||
|
|
||||||
|
const chunkOfSize = (size: number) => {
|
||||||
|
return new TextEncoder().encode('a'.repeat(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ordered byte chunker', () => {
|
||||||
|
it('should callback multiple times if added bytes matches multiple chunk sizes', async () => {
|
||||||
|
const chunkSizes = [10, 10, 10]
|
||||||
|
let receivedBytes = new Uint8Array()
|
||||||
|
let numCallbacks = 0
|
||||||
|
|
||||||
|
const chunker = new OrderedByteChunker(chunkSizes, async (bytes) => {
|
||||||
|
numCallbacks++
|
||||||
|
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
|
||||||
|
})
|
||||||
|
|
||||||
|
await chunker.addBytes(chunkOfSize(30))
|
||||||
|
|
||||||
|
expect(numCallbacks).toEqual(3)
|
||||||
|
expect(receivedBytes.length).toEqual(30)
|
||||||
|
})
|
||||||
|
})
|
||||||
40
packages/filepicker/src/Chunker/OrderedByteChunker.ts
Normal file
40
packages/filepicker/src/Chunker/OrderedByteChunker.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
export class OrderedByteChunker {
|
||||||
|
private bytes = new Uint8Array()
|
||||||
|
private index = 1
|
||||||
|
private remainingChunks: number[] = []
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private chunkSizes: number[],
|
||||||
|
private onChunk: (chunk: Uint8Array, index: number, isLast: boolean) => Promise<void>,
|
||||||
|
) {
|
||||||
|
this.remainingChunks = chunkSizes.slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
private needsPop(): boolean {
|
||||||
|
return this.remainingChunks.length > 0 && this.bytes.length >= this.remainingChunks[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
public async addBytes(bytes: Uint8Array): Promise<void> {
|
||||||
|
this.bytes = new Uint8Array([...this.bytes, ...bytes])
|
||||||
|
|
||||||
|
if (this.needsPop()) {
|
||||||
|
await this.popBytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async popBytes(): Promise<void> {
|
||||||
|
const readUntil = this.remainingChunks[0]
|
||||||
|
|
||||||
|
const chunk = this.bytes.slice(0, readUntil)
|
||||||
|
|
||||||
|
this.bytes = new Uint8Array([...this.bytes.slice(readUntil)])
|
||||||
|
|
||||||
|
this.remainingChunks.shift()
|
||||||
|
|
||||||
|
await this.onChunk(chunk, this.index++, this.index === this.chunkSizes.length - 1)
|
||||||
|
|
||||||
|
if (this.needsPop()) {
|
||||||
|
await this.popBytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
packages/filepicker/src/Classic/ClassicReader.ts
Normal file
59
packages/filepicker/src/Classic/ClassicReader.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { ByteChunker } from './../Chunker/ByteChunker'
|
||||||
|
import { OnChunkCallback, FileSelectionResponse } from '../types'
|
||||||
|
import { readFile as utilsReadFile } from '../utils'
|
||||||
|
import { FileReaderInterface } from '../Interface/FileReader'
|
||||||
|
|
||||||
|
export const ClassicFileReader: FileReaderInterface = {
|
||||||
|
selectFiles,
|
||||||
|
readFile,
|
||||||
|
available,
|
||||||
|
maximumFileSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
function available(): boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function maximumFileSize(): number {
|
||||||
|
return 50 * 1_000_000
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectFiles(): Promise<File[]> {
|
||||||
|
const input = document.createElement('input') as HTMLInputElement
|
||||||
|
input.type = 'file'
|
||||||
|
input.multiple = true
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
input.onchange = async (event) => {
|
||||||
|
const target = event.target as HTMLInputElement
|
||||||
|
const files = []
|
||||||
|
for (const file of target.files as FileList) {
|
||||||
|
files.push(file)
|
||||||
|
}
|
||||||
|
resolve(files)
|
||||||
|
}
|
||||||
|
input.click()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readFile(
|
||||||
|
file: File,
|
||||||
|
minimumChunkSize: number,
|
||||||
|
onChunk: OnChunkCallback,
|
||||||
|
): Promise<FileSelectionResponse> {
|
||||||
|
const buffer = await utilsReadFile(file)
|
||||||
|
const chunker = new ByteChunker(minimumChunkSize, onChunk)
|
||||||
|
const readSize = 2_000_000
|
||||||
|
|
||||||
|
for (let i = 0; i < buffer.length; i += readSize) {
|
||||||
|
const chunkMax = i + readSize
|
||||||
|
const chunk = buffer.slice(i, chunkMax)
|
||||||
|
const isFinalChunk = chunkMax >= buffer.length
|
||||||
|
await chunker.addBytes(chunk, isFinalChunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: file.name,
|
||||||
|
mimeType: file.type,
|
||||||
|
}
|
||||||
|
}
|
||||||
23
packages/filepicker/src/Classic/ClassicSaver.ts
Normal file
23
packages/filepicker/src/Classic/ClassicSaver.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { saveFile } from '../utils'
|
||||||
|
|
||||||
|
export class ClassicFileSaver {
|
||||||
|
public loggingEnabled = false
|
||||||
|
|
||||||
|
private log(...args: any[]): void {
|
||||||
|
if (!this.loggingEnabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
static maximumFileSize(): number {
|
||||||
|
return 50 * 1_000_000
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFile(name: string, bytes: Uint8Array): void {
|
||||||
|
this.log('Saving file to disk...')
|
||||||
|
saveFile(name, bytes)
|
||||||
|
this.log('Closing write stream')
|
||||||
|
}
|
||||||
|
}
|
||||||
11
packages/filepicker/src/Interface/FileReader.ts
Normal file
11
packages/filepicker/src/Interface/FileReader.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { OnChunkCallback, FileSelectionResponse } from '../types'
|
||||||
|
|
||||||
|
export interface FileReaderInterface {
|
||||||
|
selectFiles(): Promise<File[]>
|
||||||
|
|
||||||
|
readFile(file: File, minimumChunkSize: number, onChunk: OnChunkCallback): Promise<FileSelectionResponse>
|
||||||
|
|
||||||
|
available(): boolean
|
||||||
|
|
||||||
|
maximumFileSize(): number | undefined
|
||||||
|
}
|
||||||
112
packages/filepicker/src/Streaming/StreamingApi.ts
Normal file
112
packages/filepicker/src/Streaming/StreamingApi.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import {
|
||||||
|
FileSystemApi,
|
||||||
|
DirectoryHandle,
|
||||||
|
FileHandleReadWrite,
|
||||||
|
FileHandleRead,
|
||||||
|
FileSystemNoSelection,
|
||||||
|
FileSystemResult,
|
||||||
|
} from '@standardnotes/services'
|
||||||
|
|
||||||
|
interface WebDirectoryHandle extends DirectoryHandle {
|
||||||
|
nativeHandle: FileSystemDirectoryHandle
|
||||||
|
}
|
||||||
|
interface WebFileHandleReadWrite extends FileHandleReadWrite {
|
||||||
|
nativeHandle: FileSystemFileHandle
|
||||||
|
writableStream: FileSystemWritableFileStream
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WebFileHandleRead extends FileHandleRead {
|
||||||
|
nativeHandle: FileSystemFileHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StreamingFileApi implements FileSystemApi {
|
||||||
|
async selectDirectory(): Promise<DirectoryHandle | FileSystemNoSelection> {
|
||||||
|
try {
|
||||||
|
const nativeHandle = await window.showDirectoryPicker()
|
||||||
|
|
||||||
|
return { nativeHandle }
|
||||||
|
} catch (error) {
|
||||||
|
return 'aborted'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createFile(directory: WebDirectoryHandle, name: string): Promise<WebFileHandleReadWrite> {
|
||||||
|
const nativeHandle = await directory.nativeHandle.getFileHandle(name, { create: true })
|
||||||
|
const writableStream = await nativeHandle.createWritable()
|
||||||
|
|
||||||
|
return {
|
||||||
|
nativeHandle,
|
||||||
|
writableStream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createDirectory(
|
||||||
|
parentDirectory: WebDirectoryHandle,
|
||||||
|
name: string,
|
||||||
|
): Promise<WebDirectoryHandle | FileSystemNoSelection> {
|
||||||
|
const nativeHandle = await parentDirectory.nativeHandle.getDirectoryHandle(name, { create: true })
|
||||||
|
return { nativeHandle }
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveBytes(file: WebFileHandleReadWrite, bytes: Uint8Array): Promise<'success' | 'failed'> {
|
||||||
|
await file.writableStream.write(bytes)
|
||||||
|
|
||||||
|
return 'success'
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveString(file: WebFileHandleReadWrite, contents: string): Promise<'success' | 'failed'> {
|
||||||
|
await file.writableStream.write(contents)
|
||||||
|
|
||||||
|
return 'success'
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeFileWriteStream(file: WebFileHandleReadWrite): Promise<'success' | 'failed'> {
|
||||||
|
await file.writableStream.close()
|
||||||
|
|
||||||
|
return 'success'
|
||||||
|
}
|
||||||
|
|
||||||
|
async selectFile(): Promise<WebFileHandleRead | FileSystemNoSelection> {
|
||||||
|
try {
|
||||||
|
const selection = await window.showOpenFilePicker()
|
||||||
|
|
||||||
|
const file = selection[0]
|
||||||
|
|
||||||
|
return {
|
||||||
|
nativeHandle: file,
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
return 'aborted'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async readFile(
|
||||||
|
fileHandle: WebFileHandleRead,
|
||||||
|
onBytes: (bytes: Uint8Array, isLast: boolean) => Promise<void>,
|
||||||
|
): Promise<FileSystemResult> {
|
||||||
|
const file = await fileHandle.nativeHandle.getFile()
|
||||||
|
const stream = file.stream() as unknown as ReadableStream
|
||||||
|
const reader = stream.getReader()
|
||||||
|
|
||||||
|
let previousChunk: Uint8Array
|
||||||
|
|
||||||
|
const processChunk = async (result: ReadableStreamDefaultReadResult<Uint8Array>): Promise<void> => {
|
||||||
|
if (result.done) {
|
||||||
|
await onBytes(previousChunk, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousChunk) {
|
||||||
|
await onBytes(previousChunk, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
previousChunk = result.value
|
||||||
|
|
||||||
|
return reader.read().then(processChunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
await reader.read().then(processChunk)
|
||||||
|
|
||||||
|
return 'success'
|
||||||
|
}
|
||||||
|
}
|
||||||
75
packages/filepicker/src/Streaming/StreamingReader.ts
Normal file
75
packages/filepicker/src/Streaming/StreamingReader.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { FileReaderInterface } from './../Interface/FileReader'
|
||||||
|
import { ByteChunker } from '../Chunker/ByteChunker'
|
||||||
|
import { OnChunkCallback, FileSelectionResponse } from '../types'
|
||||||
|
|
||||||
|
interface StreamingFileReaderInterface {
|
||||||
|
getFilesFromHandles(handles: FileSystemFileHandle[]): Promise<File[]>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The File System Access API File Picker
|
||||||
|
* https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
|
||||||
|
*/
|
||||||
|
export const StreamingFileReader: StreamingFileReaderInterface & FileReaderInterface = {
|
||||||
|
getFilesFromHandles,
|
||||||
|
selectFiles,
|
||||||
|
readFile,
|
||||||
|
available,
|
||||||
|
maximumFileSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
function maximumFileSize(): number | undefined {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFilesFromHandles(handles: FileSystemFileHandle[]): Promise<File[]> {
|
||||||
|
return Promise.all(handles.map((handle) => handle.getFile()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectFiles(): Promise<File[]> {
|
||||||
|
let selectedFilesHandles: FileSystemFileHandle[]
|
||||||
|
try {
|
||||||
|
selectedFilesHandles = await window.showOpenFilePicker({ multiple: true })
|
||||||
|
} catch (error) {
|
||||||
|
selectedFilesHandles = []
|
||||||
|
}
|
||||||
|
return getFilesFromHandles(selectedFilesHandles)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readFile(
|
||||||
|
file: File,
|
||||||
|
minimumChunkSize: number,
|
||||||
|
onChunk: OnChunkCallback,
|
||||||
|
): Promise<FileSelectionResponse> {
|
||||||
|
const byteChunker = new ByteChunker(minimumChunkSize, onChunk)
|
||||||
|
const stream = file.stream() as unknown as ReadableStream
|
||||||
|
const reader = stream.getReader()
|
||||||
|
|
||||||
|
let previousChunk: Uint8Array
|
||||||
|
|
||||||
|
const processChunk = async (result: ReadableStreamDefaultReadResult<Uint8Array>): Promise<void> => {
|
||||||
|
if (result.done) {
|
||||||
|
await byteChunker.addBytes(previousChunk, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousChunk) {
|
||||||
|
await byteChunker.addBytes(previousChunk, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
previousChunk = result.value
|
||||||
|
|
||||||
|
return reader.read().then(processChunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
await reader.read().then(processChunk)
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: file.name,
|
||||||
|
mimeType: file.type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function available(): boolean {
|
||||||
|
return window.showOpenFilePicker != undefined
|
||||||
|
}
|
||||||
49
packages/filepicker/src/Streaming/StreamingSaver.ts
Normal file
49
packages/filepicker/src/Streaming/StreamingSaver.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* The File System Access API File Picker
|
||||||
|
* https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
|
||||||
|
*/
|
||||||
|
export class StreamingFileSaver {
|
||||||
|
public loggingEnabled = false
|
||||||
|
private writableStream!: FileSystemWritableFileStream
|
||||||
|
|
||||||
|
constructor(private name: string) {}
|
||||||
|
|
||||||
|
private log(...args: any[]): void {
|
||||||
|
if (!this.loggingEnabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
static available(): boolean {
|
||||||
|
return window.showSaveFilePicker != undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This function must be called in response to a user interaction, otherwise, it will be rejected by the browser. */
|
||||||
|
async selectFileToSaveTo(): Promise<void> {
|
||||||
|
this.log('Showing save file picker')
|
||||||
|
|
||||||
|
const downloadHandle = await window.showSaveFilePicker({
|
||||||
|
suggestedName: this.name,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.writableStream = await downloadHandle.createWritable()
|
||||||
|
}
|
||||||
|
|
||||||
|
async pushBytes(bytes: Uint8Array): Promise<void> {
|
||||||
|
if (!this.writableStream) {
|
||||||
|
throw Error('Must call selectFileToSaveTo first')
|
||||||
|
}
|
||||||
|
this.log('Writing chunk to disk of size', bytes.length)
|
||||||
|
await this.writableStream.write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
async finish(): Promise<void> {
|
||||||
|
if (!this.writableStream) {
|
||||||
|
throw Error('Must call selectFileToSaveTo first')
|
||||||
|
}
|
||||||
|
this.log('Closing write stream')
|
||||||
|
await this.writableStream.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
7
packages/filepicker/src/TypedBytes.ts
Normal file
7
packages/filepicker/src/TypedBytes.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export type EncryptedBytes = {
|
||||||
|
encryptedBytes: Uint8Array
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DecryptedBytes = {
|
||||||
|
decryptedBytes: Uint8Array
|
||||||
|
}
|
||||||
11
packages/filepicker/src/index.ts
Normal file
11
packages/filepicker/src/index.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export * from './types'
|
||||||
|
export * from './Classic/ClassicReader'
|
||||||
|
export * from './Classic/ClassicSaver'
|
||||||
|
export * from './Streaming/StreamingReader'
|
||||||
|
export * from './Streaming/StreamingSaver'
|
||||||
|
export * from './Streaming/StreamingApi'
|
||||||
|
export * from './utils'
|
||||||
|
export * from './Chunker/ByteChunker'
|
||||||
|
export * from './Chunker/OrderedByteChunker'
|
||||||
|
export * from './Cache/FileMemoryCache'
|
||||||
|
export * from './TypedBytes'
|
||||||
6
packages/filepicker/src/types.ts
Normal file
6
packages/filepicker/src/types.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export type OnChunkCallback = (chunk: Uint8Array, index: number, isLast: boolean) => Promise<void>
|
||||||
|
|
||||||
|
export type FileSelectionResponse = {
|
||||||
|
name: string
|
||||||
|
mimeType: string
|
||||||
|
}
|
||||||
77
packages/filepicker/src/utils.spec.ts
Normal file
77
packages/filepicker/src/utils.spec.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { formatSizeToReadableString, parseFileName } from './utils'
|
||||||
|
|
||||||
|
describe('utils', () => {
|
||||||
|
describe('parseFileName', () => {
|
||||||
|
it('should parse regular filenames', () => {
|
||||||
|
const fileName = 'test.txt'
|
||||||
|
|
||||||
|
const { name, ext } = parseFileName(fileName)
|
||||||
|
|
||||||
|
expect(name).toBe('test')
|
||||||
|
expect(ext).toBe('txt')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse filenames with multiple dots', () => {
|
||||||
|
const fileName = 'Screen Shot 2022-03-06 at 12.13.32 PM.png'
|
||||||
|
|
||||||
|
const { name, ext } = parseFileName(fileName)
|
||||||
|
|
||||||
|
expect(name).toBe('Screen Shot 2022-03-06 at 12.13.32 PM')
|
||||||
|
expect(ext).toBe('png')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse filenames without extensions', () => {
|
||||||
|
const fileName = 'extensionless'
|
||||||
|
|
||||||
|
const { name, ext } = parseFileName(fileName)
|
||||||
|
|
||||||
|
expect(name).toBe('extensionless')
|
||||||
|
expect(ext).toBe('')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('formatSizeToReadableString', () => {
|
||||||
|
it('should show as bytes if less than 1KB', () => {
|
||||||
|
const size = 1_023
|
||||||
|
|
||||||
|
const formattedSize = formatSizeToReadableString(size)
|
||||||
|
|
||||||
|
expect(formattedSize).toBe('1023 B')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should format as KB', () => {
|
||||||
|
const size = 1_024
|
||||||
|
|
||||||
|
const formattedSize = formatSizeToReadableString(size)
|
||||||
|
|
||||||
|
expect(formattedSize).toBe('1 KB')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should format as MB', () => {
|
||||||
|
const size = 1_048_576
|
||||||
|
|
||||||
|
const formattedSize = formatSizeToReadableString(size)
|
||||||
|
|
||||||
|
expect(formattedSize).toBe('1 MB')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should format as GB', () => {
|
||||||
|
const size = 1_073_741_824
|
||||||
|
|
||||||
|
const formattedSize = formatSizeToReadableString(size)
|
||||||
|
|
||||||
|
expect(formattedSize).toBe('1 GB')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should only show fixed-point notation if calculated size is not an integer', () => {
|
||||||
|
const size1 = 1_048_576
|
||||||
|
const size2 = 1_572_864
|
||||||
|
|
||||||
|
const formattedSize1 = formatSizeToReadableString(size1)
|
||||||
|
const formattedSize2 = formatSizeToReadableString(size2)
|
||||||
|
|
||||||
|
expect(formattedSize1).toBe('1 MB')
|
||||||
|
expect(formattedSize2).toBe('1.50 MB')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
56
packages/filepicker/src/utils.ts
Normal file
56
packages/filepicker/src/utils.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
export async function readFile(file: File): Promise<Uint8Array> {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.readAsArrayBuffer(file)
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
reader.onload = (readerEvent) => {
|
||||||
|
const target = readerEvent.target as FileReader
|
||||||
|
const content = target.result as ArrayBuffer
|
||||||
|
resolve(new Uint8Array(content))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseFileName(fileName: string): {
|
||||||
|
name: string
|
||||||
|
ext: string
|
||||||
|
} {
|
||||||
|
const pattern = /(?:\.([^.]+))?$/
|
||||||
|
const extMatches = pattern.exec(fileName)
|
||||||
|
const ext = extMatches?.[1] || ''
|
||||||
|
const name = fileName.includes('.') ? fileName.substring(0, fileName.lastIndexOf('.')) : fileName
|
||||||
|
|
||||||
|
return { name, ext }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveFile(name: string, bytes: Uint8Array): void {
|
||||||
|
const link = document.createElement('a')
|
||||||
|
const blob = new Blob([bytes], {
|
||||||
|
type: 'text/plain;charset=utf-8',
|
||||||
|
})
|
||||||
|
link.href = window.URL.createObjectURL(blob)
|
||||||
|
link.setAttribute('download', name)
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
link.remove()
|
||||||
|
window.URL.revokeObjectURL(link.href)
|
||||||
|
}
|
||||||
|
|
||||||
|
const BYTES_IN_ONE_KILOBYTE = 1_024
|
||||||
|
const BYTES_IN_ONE_MEGABYTE = 1_048_576
|
||||||
|
const BYTES_IN_ONE_GIGABYTE = 1_073_741_824
|
||||||
|
|
||||||
|
export function formatSizeToReadableString(bytes: number): string {
|
||||||
|
let size = bytes
|
||||||
|
let unit = 'B'
|
||||||
|
if (bytes >= BYTES_IN_ONE_GIGABYTE) {
|
||||||
|
size = bytes / BYTES_IN_ONE_GIGABYTE
|
||||||
|
unit = 'GB'
|
||||||
|
} else if (bytes >= BYTES_IN_ONE_MEGABYTE) {
|
||||||
|
size = bytes / BYTES_IN_ONE_MEGABYTE
|
||||||
|
unit = 'MB'
|
||||||
|
} else if (bytes >= BYTES_IN_ONE_KILOBYTE) {
|
||||||
|
size = bytes / BYTES_IN_ONE_KILOBYTE
|
||||||
|
unit = 'KB'
|
||||||
|
}
|
||||||
|
return `${Number.isInteger(size) ? size : size.toFixed(2)} ${unit}`
|
||||||
|
}
|
||||||
13
packages/filepicker/tsconfig.json
Normal file
13
packages/filepicker/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../node_modules/@standardnotes/config/src/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./dist",
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"references": [],
|
||||||
|
"exclude": ["**/*.spec.ts", "dist", "example"]
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ module.exports = (async () => {
|
|||||||
} = await getDefaultConfig()
|
} = await getDefaultConfig()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
watchFolders: [__dirname, '../icons', '../styles', '../components', '../features', '../encryption'],
|
watchFolders: [__dirname, '../icons', '../styles', '../components', '../features', '../encryption', '../filepicker'],
|
||||||
transformer: {
|
transformer: {
|
||||||
getTransformOptions: async () => ({
|
getTransformOptions: async () => ({
|
||||||
transform: {
|
transform: {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
"@standardnotes/components-meta": "workspace:*",
|
"@standardnotes/components-meta": "workspace:*",
|
||||||
"@standardnotes/encryption": "workspace:*",
|
"@standardnotes/encryption": "workspace:*",
|
||||||
"@standardnotes/features": "workspace:*",
|
"@standardnotes/features": "workspace:*",
|
||||||
"@standardnotes/filepicker": "^1.16.23",
|
"@standardnotes/filepicker": "workspace:*",
|
||||||
"@standardnotes/icons": "workspace:*",
|
"@standardnotes/icons": "workspace:*",
|
||||||
"@standardnotes/react-native-aes": "^1.4.3",
|
"@standardnotes/react-native-aes": "^1.4.3",
|
||||||
"@standardnotes/react-native-textview": "1.1.0",
|
"@standardnotes/react-native-textview": "1.1.0",
|
||||||
|
|||||||
1
packages/web/.gitignore
vendored
1
packages/web/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
dist
|
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
"@reach/tooltip": "^0.16.2",
|
"@reach/tooltip": "^0.16.2",
|
||||||
"@reach/visually-hidden": "^0.16.0",
|
"@reach/visually-hidden": "^0.16.0",
|
||||||
"@standardnotes/encryption": "workspace:*",
|
"@standardnotes/encryption": "workspace:*",
|
||||||
"@standardnotes/filepicker": "1.16.23",
|
"@standardnotes/filepicker": "workspace:*",
|
||||||
"@standardnotes/icons": "workspace:*",
|
"@standardnotes/icons": "workspace:*",
|
||||||
"@standardnotes/services": "^1.13.23",
|
"@standardnotes/services": "^1.13.23",
|
||||||
"@standardnotes/sncrypto-web": "1.10.1",
|
"@standardnotes/sncrypto-web": "1.10.1",
|
||||||
|
|||||||
72
yarn.lock
72
yarn.lock
@@ -6599,27 +6599,23 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@standardnotes/filepicker@npm:1.16.23, @standardnotes/filepicker@npm:^1.16.23":
|
"@standardnotes/filepicker@^1.16.22, @standardnotes/filepicker@^1.16.23, @standardnotes/filepicker@workspace:*, @standardnotes/filepicker@workspace:packages/filepicker":
|
||||||
version: 1.16.23
|
version: 0.0.0-use.local
|
||||||
resolution: "@standardnotes/filepicker@npm:1.16.23"
|
resolution: "@standardnotes/filepicker@workspace:packages/filepicker"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@standardnotes/common": ^1.23.1
|
"@standardnotes/common": ^1.23.1
|
||||||
"@standardnotes/services": ^1.13.23
|
"@standardnotes/services": ^1.13.23
|
||||||
"@standardnotes/utils": ^1.6.12
|
"@standardnotes/utils": ^1.6.12
|
||||||
checksum: 8b1eca3f4ee5d821bf0e7aaa859a0d6204b1083adf168db2d9deac9984d2d4b13d04cb018c8b545d26c2a8f2fc262d140e3e43bd4c91f0c9aebabee42bcd92f7
|
"@types/jest": ^27.4.1
|
||||||
languageName: node
|
"@types/wicg-file-system-access": ^2020.9.5
|
||||||
linkType: hard
|
"@typescript-eslint/eslint-plugin": ^5.30.0
|
||||||
|
eslint-plugin-prettier: ^4.2.1
|
||||||
"@standardnotes/filepicker@npm:^1.16.22":
|
jest: ^27.5.1
|
||||||
version: 1.16.22
|
reflect-metadata: ^0.1.13
|
||||||
resolution: "@standardnotes/filepicker@npm:1.16.22"
|
ts-jest: ^27.1.3
|
||||||
dependencies:
|
ts-node: ^10.5.0
|
||||||
"@standardnotes/common": ^1.23.1
|
languageName: unknown
|
||||||
"@standardnotes/services": ^1.13.22
|
linkType: soft
|
||||||
"@standardnotes/utils": ^1.6.12
|
|
||||||
checksum: df14eedefd9d9a5ebdff483b611eac2c78a6a17e2039b5893a9041b0abbf88acf85aa36444cbf5b3b0c52a1556a175d20ed15a2879117895d3669b597ee07d60
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@standardnotes/files@npm:^1.3.22":
|
"@standardnotes/files@npm:^1.3.22":
|
||||||
version: 1.3.22
|
version: 1.3.22
|
||||||
@@ -6936,7 +6932,7 @@ __metadata:
|
|||||||
"@standardnotes/config": ^2.4.3
|
"@standardnotes/config": ^2.4.3
|
||||||
"@standardnotes/encryption": "workspace:*"
|
"@standardnotes/encryption": "workspace:*"
|
||||||
"@standardnotes/features": "workspace:*"
|
"@standardnotes/features": "workspace:*"
|
||||||
"@standardnotes/filepicker": ^1.16.23
|
"@standardnotes/filepicker": "workspace:*"
|
||||||
"@standardnotes/icons": "workspace:*"
|
"@standardnotes/icons": "workspace:*"
|
||||||
"@standardnotes/react-native-aes": ^1.4.3
|
"@standardnotes/react-native-aes": ^1.4.3
|
||||||
"@standardnotes/react-native-textview": 1.1.0
|
"@standardnotes/react-native-textview": 1.1.0
|
||||||
@@ -7431,7 +7427,7 @@ __metadata:
|
|||||||
"@reach/tooltip": ^0.16.2
|
"@reach/tooltip": ^0.16.2
|
||||||
"@reach/visually-hidden": ^0.16.0
|
"@reach/visually-hidden": ^0.16.0
|
||||||
"@standardnotes/encryption": "workspace:*"
|
"@standardnotes/encryption": "workspace:*"
|
||||||
"@standardnotes/filepicker": 1.16.23
|
"@standardnotes/filepicker": "workspace:*"
|
||||||
"@standardnotes/icons": "workspace:*"
|
"@standardnotes/icons": "workspace:*"
|
||||||
"@standardnotes/services": ^1.13.23
|
"@standardnotes/services": ^1.13.23
|
||||||
"@standardnotes/sncrypto-web": 1.10.1
|
"@standardnotes/sncrypto-web": 1.10.1
|
||||||
@@ -36906,6 +36902,44 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ts-node@npm:^10.5.0":
|
||||||
|
version: 10.8.2
|
||||||
|
resolution: "ts-node@npm:10.8.2"
|
||||||
|
dependencies:
|
||||||
|
"@cspotcode/source-map-support": ^0.8.0
|
||||||
|
"@tsconfig/node10": ^1.0.7
|
||||||
|
"@tsconfig/node12": ^1.0.7
|
||||||
|
"@tsconfig/node14": ^1.0.0
|
||||||
|
"@tsconfig/node16": ^1.0.2
|
||||||
|
acorn: ^8.4.1
|
||||||
|
acorn-walk: ^8.1.1
|
||||||
|
arg: ^4.1.0
|
||||||
|
create-require: ^1.1.0
|
||||||
|
diff: ^4.0.1
|
||||||
|
make-error: ^1.1.1
|
||||||
|
v8-compile-cache-lib: ^3.0.1
|
||||||
|
yn: 3.1.1
|
||||||
|
peerDependencies:
|
||||||
|
"@swc/core": ">=1.2.50"
|
||||||
|
"@swc/wasm": ">=1.2.50"
|
||||||
|
"@types/node": "*"
|
||||||
|
typescript: ">=2.7"
|
||||||
|
peerDependenciesMeta:
|
||||||
|
"@swc/core":
|
||||||
|
optional: true
|
||||||
|
"@swc/wasm":
|
||||||
|
optional: true
|
||||||
|
bin:
|
||||||
|
ts-node: dist/bin.js
|
||||||
|
ts-node-cwd: dist/bin-cwd.js
|
||||||
|
ts-node-esm: dist/bin-esm.js
|
||||||
|
ts-node-script: dist/bin-script.js
|
||||||
|
ts-node-transpile-only: dist/bin-transpile.js
|
||||||
|
ts-script: dist/bin-script-deprecated.js
|
||||||
|
checksum: 1eede939beed9f4db35bcc88d78ef803815b99dcdbed1ecac728d861d74dc694918a7f0f437aa08d026193743a31e7e00e2ee34f875f909b5879981c1808e2a7
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"ts-node@npm:^10.7.0, ts-node@npm:^10.8.1":
|
"ts-node@npm:^10.7.0, ts-node@npm:^10.8.1":
|
||||||
version: 10.8.1
|
version: 10.8.1
|
||||||
resolution: "ts-node@npm:10.8.1"
|
resolution: "ts-node@npm:10.8.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user