Skip to content

Commit 847b6c7

Browse files
committed
Big update
1 parent 1f0e85e commit 847b6c7

32 files changed

+1953
-2091
lines changed

.github/FUNDING.yml

Whitespace-only changes.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,4 @@ dist
104104
.tern-port
105105

106106
.idea
107+
.history

.vscode/settings.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"files.insertFinalNewline": true,
3+
"files.trimFinalNewlines": true,
4+
"files.trimTrailingWhitespace": true,
5+
"editor.tabSize": 2,
6+
"javascript.preferences.quoteStyle": "single",
7+
"javascript.format.insertSpaceAfterConstructor": true,
8+
"javascript.format.insertSpaceAfterCommaDelimiter": true,
9+
"javascript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": true,
10+
"javascript.format.semicolons": "remove",
11+
"javascript.suggest.completeFunctionCalls": true,
12+
"javascript.suggest.completeJSDocs": true,
13+
"javascript.suggest.autoImports": true,
14+
"js/ts.implicitProjectConfig.checkJs": true,
15+
}

README.md

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
# Native File System adapter (ponyfill)
22

3-
> This is an in-browser file system that follows [native-file-system](https://wicg.github.io/native-file-system/) and supports storing and retrieving files from various backends.
3+
What is this?
4+
5+
This is file system that follows [native-file-system](https://wicg.github.io/native-file-system/) specification. Thanks to it we can have a unified way of handling data in all browser and even in NodeJS in a more secure way.
6+
7+
At a high level what we're providing is several bits:
8+
9+
1. A modernized version of `FileSystemFileHandle` and `FileSystemDirectoryHandle` interfaces.
10+
2. A modernized version of `FileSystemWritableFileStream` to truncate and write data.
11+
3. A way to not only use one location to read & write data to and from, but several other sources called adapters
412

513
### Adapters
614

7-
This polyfill/ponyfill ships with 5 filesystem backends:
15+
This polyfill/ponyfill ships with a few backends built in:
816

9-
* `native`: Stores files the `Native Sandboxed` file storage
17+
* `node`: Interact with filesystem using nodes `fs`
18+
* `native`: Stores files the `Native Sandboxed` file file system storage
1019
* `Sandbox`: Stores files into the Blinks `Sandboxed FileSystem` API.
1120
* `IndexedDB`: Stores files into the browser's `IndexedDB` object database.
1221
* `Memory`: Stores files in-memory. Thus, it is a temporary file store that clears when the user navigates away.
1322
* `Cache storage`: Stores files in cache storage like a request/response a-like.
1423

24+
You can even load in your own underlying adapter and get the same set of API's
25+
1526
The api is designed in such a way that it can work with or without the ponyfill if you choose to remove or add this.<br>
16-
It's not trying to interfear with the changing spec by using other properties that may conflict with the feature changes to the spec. A few none spec options are prefixed with a `_`
27+
It's not trying to interfere with the changing spec by using other properties that may conflict with the feature changes to the spec.
1728

18-
( The current minium supported browser I have choosen to support is the ones that can handle import/export )<br>
29+
( The current minium supported browser I have chosen to support is the ones that can handle import/export )<br>
1930
( Some parts are lazy loaded when needed )
2031

2132
### Using
@@ -32,34 +43,42 @@ import {
3243
getOriginPrivateDirectory
3344
} from 'https://cdn.jsdelivr.net/gh/jimmywarting/native-file-system-adapter/src/es6.js'
3445

35-
3646
// the getOriginPrivateDirectory is a legacy name that
3747
// native filesystem added, have not bother to change it
3848

39-
getOriginPrivateDirectory() // same as calling navigator.storage.getDirectory()
40-
// Blink's good old sandboxed file system API, can choose between persistent and temporary
41-
getOriginPrivateDirectory(import('../src/adapters/sandbox.js'))
42-
getOriginPrivateDirectory(import('../src/adapters/memory.js'))
43-
getOriginPrivateDirectory(import('../src/adapters/indexeddb.js'))
44-
getOriginPrivateDirectory(import('../src/adapters/cache.js'))
45-
getOriginPrivateDirectory(import('../src/adapters/node.js'), './starting-path')
49+
// same as calling navigator.storage.getDirectory()
50+
handle = await getOriginPrivateDirectory()
51+
// A write only adapter to save files to the disk
52+
handle = await getOriginPrivateDirectory(import('../src/adapters/downloader.js'))
53+
// Blinks old sandboxed api
54+
handle = await getOriginPrivateDirectory(import('../src/adapters/sandbox.js'))
55+
// fast in-memory file system
56+
handle = await getOriginPrivateDirectory(import('../src/adapters/memory.js'))
57+
// Using indexDB
58+
handle = await getOriginPrivateDirectory(import('../src/adapters/indexeddb.js'))
59+
// Store things in the new Cache API as request/responses (bad at mutating data)
60+
handle = await getOriginPrivateDirectory(import('../src/adapters/cache.js'))
61+
62+
// Node only variant:
63+
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/src/adapters/memory.js'))
64+
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/src/adapters/node.js'), './starting-path')
65+
66+
4667

4768
// The polyfilled (file input) version will turn into a memory adapter
48-
// You will have readwrite permission on the memory adapter,
69+
// You will have read & write permission on the memory adapter,
4970
// you might want to transfer (copy) the handle to another adapter
5071
showOpenFilePicker({_preferPolyfill: boolean, ...sameOpts}).then(fileHandle => {})
5172
showDirectoryPicker({_preferPolyfill: boolean, ...sameOpts}).then(directoryHandle => {})
5273

5374
// Supports drag and drop also
54-
ondrop = evt => {
75+
window.ondrop = evt => {
5576
evt.preventDefault()
56-
getOriginPrivateDirectory(evt.dataTransfer).then(directoryHandle => {
57-
// This is kind of a hybrid memory & sandboxed (Entry api) adapter
58-
// it works together with old Entry API rather then transferring all of it to a memory adapter
59-
// This would allow you to monitor file changes by calling getFile()
60-
// and compare the last modified date or file size
61-
// You will have read access but, requesting write permission will reject.
62-
})
77+
for (let item of evt.dataTransfer.items) {
78+
item.getAsFileSystemHandle(handle => {
79+
console.log(handle)
80+
})
81+
}
6382
}
6483

6584
// request user to select a file
@@ -73,10 +92,11 @@ const fileHandle = await showOpenFilePicker({
7392
// returns a File Instance
7493
const file = await fileHandle.getFile()
7594

76-
// copy the file over to a another adapter
77-
const rootHandle = await getOriginPrivateDirectory() // same as navigator.store.getDirectory()
95+
// copy the file over to a another place
96+
const rootHandle = await getOriginPrivateDirectory()
7897
const fileHandle = await rootHandle.getFileHandle(file.name, { create: true })
7998
await fileHandle.write(file)
99+
fileHandle.close()
80100

81101
// save/download a file
82102
const fileHandle = await showSaveFilePicker({
@@ -90,7 +110,7 @@ const fileHandle = await showSaveFilePicker({
90110
excludeAcceptAllOption: false // default
91111
})
92112

93-
// Look at what extension they chosed
113+
// Look at what extension they chosen
94114
const extensionChosen = fileHandle.name.split('.').pop()
95115

96116
const blob = {
@@ -100,7 +120,7 @@ const blob = {
100120
}[extensionChosen]
101121

102122
await blob.stream().pipeTo(fileHandle.getWriter())
103-
// or
123+
// or
104124
var writer = fileHandle.getWriter()
105125
writer.write(blob)
106126
writer.close()
@@ -113,7 +133,7 @@ Will get to it at some point in the feature
113133

114134
Saving/downloading a file borrowing some of ideas from [StreamSaver.js](https://github.com/jimmywarting/StreamSaver.js).
115135
The difference is:
116-
- Using service worker is optional choice with this adapter. 🤷‍
136+
- Using service worker is optional choice with this adapter.
117137
- It dose not rely on some man-in-the-middle or external hosted service worker.
118138
- If you want to stream large data to the disk directly instead of taking up much RAM you need to set up a service worker yourself.<br>(note that it requires https - but again worker is optional)
119139
- You don't have to handle web-streams-polyfills it's lazy loaded when needed when you need that writable stream. 😴
@@ -126,17 +146,12 @@ navigator.serviceWorker.register('sw.js')
126146

127147
Without service worker you are going to write all data to the memory and download it once it closes.
128148

129-
Seeking and truncating won't do anything. You should be writing all data in sequental order when using the polyfilled version.
130-
131-
-----
132-
133-
If you have chosen to `open-directory` when the polyfilled version is in use (`input[webkitdirectory]`)
134-
than you can't get any write access to it. So unless you are using chanary with experimental flags or enabled the [Origin Trials](https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md) for beta testing on your origin, then you better use `save-file` instead to be safe. It's also possible to query/request permission.
149+
Seeking and truncating won't do anything. You should be writing all data in sequential order when using the polyfilled version.
135150

136151
### Testing
137152

138-
start up a server and open `/examples/test.html` in your browser.
139-
153+
- start up a server and open `/examples/test.html` in your browser.
154+
- for node: `npm i && npm run test`
140155

141156
### Resources
142157

example/ps.html

Lines changed: 0 additions & 98 deletions
This file was deleted.

example/sw.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,34 @@ const ABORT = 1
55
const CLOSE = 2
66
const PING = 3
77

8+
/** @implements {UnderlyingSource} */
89
class MessagePortSource {
10+
11+
/** @type {ReadableStreamController<any>} controller */
12+
controller
13+
14+
/** @param {MessagePort} port */
915
constructor (port) {
1016
this.port = port;
1117
this.port.onmessage = evt => this.onMessage(evt.data)
1218
}
19+
20+
/**
21+
* @param {ReadableStreamController<any>} controller
22+
*/
1323
start (controller) {
1424
this.controller = controller
1525
}
16-
pull () {
17-
this.port.postMessage({ type: PULL })
18-
}
26+
27+
/** @param {Error} reason */
1928
cancel (reason) {
20-
// Firefox can notifiy a cancel event, chrome can't
29+
// Firefox can notify a cancel event, chrome can't
2130
// https://bugs.chromium.org/p/chromium/issues/detail?id=638494
2231
this.port.postMessage({ type: ERROR, reason: reason.message })
2332
this.port.close()
2433
}
34+
35+
/** @param {{ type: number; chunk: Uint8Array; reason: any; }} message */
2536
onMessage (message) {
2637
// enqueue() will call pull() if needed when there's no backpressure
2738
if (message.type === WRITE) this.controller.enqueue(message.chunk)

example/test.html

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
// scope: './'
5050
})
5151
</script>
52-
<script type=module src="./test.js"></script>
52+
<script type="module" src="./test.js"></script>
5353
<table id="table">
5454
<caption>Browser storage</caption>
5555
<thead><tr>
@@ -135,13 +135,15 @@
135135
<tr>
136136
<td width="1">
137137
<center>Drag and drop</center><br>
138-
The DataTransfer are very similar to Blinks sandboxed filesystem.
139-
so it would be easy to just convert the event to a FileSystemDirectoryHandle
140-
and use the sandbox adapter on it.
138+
The DataTransfer have implemented <code>DataTransferItem.prototype.getAsFileSystemHandle()</code>
139+
That lets you convert a item into a filesystem handle. This is polyfilled for you.
141140
<pre style="background: #fff; box-sizing: border-box; color: #68615e; display: block; height: 100%; overflow-x: auto; padding: 0.5em;"><code style="height: 100%;">elm.ondragover = <span class="hljs-function"><span style="color: #df5320;">evt</span> =&gt;</span> evt.preventDefault()
142-
elm.ondrop = <span class="hljs-function"><span style="color: #df5320;">evt</span> =&gt;</span> {
141+
elm.ondrop = <span class="hljs-function"><span style="color: #df5320;">async evt</span> =&gt;</span> {
143142
evt.preventDefault()
144-
getOriginPrivateDirectory(evt.dataTransfer)
143+
for (const dataTransferItem of dataTransfer.items) {
144+
const handle = await dataTransferItem.getAsFileSystemHandle()
145+
console.log(handle.kind)
146+
}
145147
}</code></pre>
146148
</td>
147149
<td>Drop anywhere<br>on page</td>

0 commit comments

Comments
 (0)