Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

native-file-system-adapter

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

native-file-system-adapter - npm Package Compare versions

Comparing version 1.0.1 to 1.1.0

mod.js

5

example/sw.js

@@ -38,3 +38,6 @@ const WRITE = 0

// enqueue() will call pull() if needed when there's no backpressure
if (message.type === WRITE) this.controller.enqueue(message.chunk)
if (message.type === WRITE) {
this.controller.enqueue(message.chunk)
this.port.postMessage({ type: PULL })
}
if (message.type === ABORT) {

@@ -41,0 +44,0 @@ this.controller.error(message.reason)

46

example/test.js

@@ -96,4 +96,4 @@ import * as fs from '../src/es6.js'

accept: {
'text/plain': ['txt', 'text'],
'text/html': ['html', 'htm']
'text/plain': ['.txt', '.text'],
'text/html': ['.html', '.htm']
}

@@ -104,3 +104,3 @@ },

accept: {
'image/*': ['png', 'gif', 'jpeg', 'jpg']
'image/*': ['.png', '.gif', '.jpeg', '.jpg']
}

@@ -111,11 +111,5 @@ }

$types2.value = JSON.stringify([
{
accept: { 'image/jpg': ['jpg'] }
},
{
accept: { 'image/png': ['png'] }
},
{
accept: { 'image/webp': ['webp'] }
}
{ accept: { 'image/jpg': ['.jpg'] } },
{ accept: { 'image/png': ['.png'] } },
{ accept: { 'image/webp': ['.webp'] } }
], null, 2)

@@ -136,3 +130,9 @@

opts._preferPolyfill = !!opts._preferPolyfill
showOpenFilePicker(opts).then(console.log, console.error)
showOpenFilePicker(opts).then(handles => {
console.log(handles)
alert(handles)
}, err => {
console.error(err)
alert(err)
})
}

@@ -149,4 +149,4 @@ form_showSaveFilePicker.onsubmit = async evt => {

const ws = await handle.createWritable()
ws.write(image)
ws.close()
await ws.write(image)
await ws.close()
}

@@ -165,3 +165,6 @@

j++
if (driver.status === 'rejected') continue
if (driver.status === 'rejected') {
console.error('Driver failed to load:' + driver.reason)
continue
}
const root = driver.value

@@ -194,6 +197,15 @@ await cleanupSandboxedFileSystem(root)

for (const item of evt.dataTransfer.items) {
item.getAsFileSystemHandle().then(showFileStructure)
item.getAsFileSystemHandle().then(async handle => {
if (handle.kind === 'directory') {
showFileStructure(handle)
} else {
const file = await handle.getFile()
console.log(file)
alert(file)
}
})
}
}
/** @param {fs.FileSystemDirectoryHandle} root */
async function showFileStructure (root) {

@@ -200,0 +212,0 @@ const result = []

{
"name": "native-file-system-adapter",
"version": "1.0.1",
"version": "1.1.0",
"description": "Native File System API",

@@ -13,5 +13,5 @@ "main": "src/es6.js",

"test": "exit 0",
"test:browser": "airtap --local test/browser.js",
"test:server": "node test/server-side.js",
"test:memory": "node test/memory.js",
"test:browser": "php -S localhost:4444 & open http://localhost:4444/example/test.html",
"test:node": "node test/test-node.js",
"test:deno": "deno run --allow-read test/test-deno.js",
"build": "rm -rf dist && rollup -c rollup.config.js"

@@ -23,3 +23,14 @@ },

},
"keywords": ["filesystem", "file", "blob", "stream", "fs", "read", "write", "delete", "file system access", "spec"],
"keywords": [
"filesystem",
"file",
"blob",
"stream",
"fs",
"read",
"write",
"delete",
"file system access",
"spec"
],
"author": "Jimmy Wärting",

@@ -30,5 +41,10 @@ "license": "MIT",

},
"contributors": [
{
"name": "Alexandru Ciuca"
}
],
"homepage": "https://github.com/jimmywarting/native-file-system-adapter#readme",
"optionalDependencies": {
"web-streams-polyfill": "^2.1.1"
"web-streams-polyfill": "^3.1.1"
},

@@ -44,3 +60,3 @@ "dependencies": {

"rollup-plugin-terser": "^7.0.2",
"standardjs": "*",
"standard": "^16.0.3",
"tape": "^5.2.2"

@@ -47,0 +63,0 @@ },

@@ -5,25 +5,28 @@ # Native File System adapter (ponyfill)

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 & Deno in a more secure way.
This is a file system API that follows the [File System Access](https://wicg.github.io/file-system-access/) specification. Thanks to it we can have a unified way of handling data in all browsers and even in NodeJS & Deno in a more secure way.
At a high level what we're providing is several bits:
1. A modernized version of `FileSystemFileHandle` and `FileSystemDirectoryHandle` interfaces.
2. A modernized version of `FileSystemWritableFileStream` to truncate and write data.
3. A way to not only use one location to read & write data to and from, but several other sources called adapters
1. Ponyfills for `showDirectoryPicker`, `showOpenFilePicker` and `showSaveFilePicker`, with fallbacks to regular input elements.
2. Ponyfills for `FileSystemFileHandle` and `FileSystemDirectoryHandle` interfaces.
3. Ponyfill for `FileSystemWritableFileStream` to truncate and write data.
4. An implementation of `navigator.storage.getDirectory()` (`getOriginPrivateDirectory`) which can read & write data to and from several sources called adapters, not just the browser sandboxed file system
5. An polyfill for `DataTransferItem.prototype.getAsFileSystemHandle()`
### Adapters
## File system adapters
This polyfill/ponyfill ships with a few backends built in:
When `getOriginPrivateDirectory` is called with no arguments, the browser's native sandboxed file system is used, just like calling `navigator.storage.getDirectory()`.
* `node`: Interact with filesystem using NodeJS `fs`
Optionally, a file system backend adapter can be provided as an argument. This ponyfill ships with a few backends built in:
* `node`: Uses NodeJS's `fs` module
* `deno`: Interact with filesystem using Deno
* `native`: Stores files the `Native Sandboxed` file file system storage
* `Sandbox`: Stores files into the Blinks `Sandboxed FileSystem` API.
* `IndexedDB`: Stores files into the browser's `IndexedDB` object database.
* `Memory`: Stores files in-memory. Thus, it is a temporary file store that clears when the user navigates away.
* `Cache storage`: Stores files in cache storage like a request/response a-like.
* `sandbox` (deprecated): Uses [requestFileSystem](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestFileSystem). Only supported in Chromium-based browsers using the `Blink` engine.
* `indexeddb`: Stores files into the browser's `IndexedDB` object database.
* `memory`: Stores files in-memory. Thus, it is a temporary file store that clears when the user navigates away.
* `cache`: Stores files with the browser's [Cache API](https://web.dev/cache-api-quick-guide/) in request/response pairs.
You can even load in your own underlying adapter and get the same set of API's
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>
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>
It's not trying to interfere with the changing spec by using other properties that may conflict with the feature changes to the spec.

@@ -34,59 +37,92 @@

### Using
### ES import in browser
```html
<script type="module">
import { getOriginPrivateDirectory } from 'https://cdn.jsdelivr.net/npm/native-file-system-adapter/mod.js'
// Get a directory handle for a sandboxed virtual file system
// same as calling navigator.storage.getDirectory()
const dirHandle1 = await getOriginPrivateDirectory()
// or use an adapter (see adapters table above for a list of available adapters)
const dirHandle2 = await getOriginPrivateDirectory(import('https://cdn.jsdelivr.net/npm/native-file-system-adapter/src/adapters/<adapterName>.js'))
</script>
```
```
npm i native-file-system-adapter
```
```js
import {
showDirectoryPicker,
showOpenFilePicker,
showSaveFilePicker,
FileSystemDirectoryHandle,
FileSystemFileHandle,
FileSystemHandle,
FileSystemWritableFileStream,
getOriginPrivateDirectory
} from 'https://cdn.jsdelivr.net/gh/jimmywarting/native-file-system-adapter/src/es6.js'
import { getOriginPrivateDirectory } from 'native-file-system-adapter'
import indexedDbAdapter from 'native-file-system-adapter/lib/adapters/indexeddb.js'
import nodeAdapter from 'native-file-system-adapter/lib/adapters/node.js'
const dirHandle = await getOriginPrivateDirectory(indexedDbAdapter)
const nodeDirHandle = await getOriginPrivateDirectory(nodeAdapter, './real-dir')
```
// the getOriginPrivateDirectory is a legacy name that
// native filesystem added, have not bother to change it
## Examples
// same as calling navigator.storage.getDirectory()
handle = await getOriginPrivateDirectory()
// A write only adapter to save files to the disk
handle = await getOriginPrivateDirectory(import('../src/adapters/downloader.js'))
// Blinks old sandboxed api
handle = await getOriginPrivateDirectory(import('../src/adapters/sandbox.js'))
// fast in-memory file system
handle = await getOriginPrivateDirectory(import('../src/adapters/memory.js'))
// Using indexDB
handle = await getOriginPrivateDirectory(import('../src/adapters/indexeddb.js'))
// Store things in the new Cache API as request/responses (bad at mutating data)
handle = await getOriginPrivateDirectory(import('../src/adapters/cache.js'))
### File system sandbox
// Node only variant:
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/src/adapters/memory.js'))
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/src/adapters/node.js'), './starting-path')
You can get a directory handle to a sandboxed virtual file system using the `getOriginPrivateDirectory` function.
This is a legacy name introduced by an older `Native File System` specification and is kept for simplicity.
It is equivalent to the `navigator.storage.getDirectory()` method introduced by the later [File System Access](https://wicg.github.io/file-system-access/) spec.
// Deno only variant:
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/src/adapters/memory.js'))
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/src/adapters/deno.js'), './starting-path')
```js
import { getOriginPrivateDirectory, support } from 'native-file-system-adapter'
// Uses native implementation - same as calling navigator.storage.getDirectory()
await getOriginPrivateDirectory().catch(err => {
// native implementation not supported fallback to any of the adapters
// Blinks old sandboxed api
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/lib/adapters/sandbox.js'))
// fast in-memory file system
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/lib/adapters/memory.js'))
// Using indexDB
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/lib/adapters/indexeddb.js'))
// Store things in the new Cache API as request/responses (bad at mutating data)
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/lib/adapters/cache.js'))
// Node only variant:
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/lib/adapters/memory.js'))
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/lib/adapters/node.js'), './starting-path')
// Deno only variant:
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/src/adapters/memory.js'))
handle = await getOriginPrivateDirectory(import('native-file-system-adapter/src/adapters/deno.js'), './starting-path')
})
```
### File and directory pickers
```js
import { showDirectoryPicker, showOpenFilePicker } from 'native-file-system-adapter'
// The polyfilled (file input) version will turn into a memory adapter
// You will have read & write permission on the memory adapter,
// you might want to transfer (copy) the handle to another adapter
showOpenFilePicker({_preferPolyfill: boolean, ...sameOpts}).then(fileHandle => {})
showDirectoryPicker({_preferPolyfill: boolean, ...sameOpts}).then(directoryHandle => {})
const [fileHandle] = await showOpenFilePicker({_preferPolyfill: boolean, ...sameOpts})
const dirHandle = await showDirectoryPicker({_preferPolyfill: boolean, ...sameOpts})
```
// Supports drag and drop also
window.ondrop = evt => {
### Drag and drop
```js
// DataTransferItem.prototype.getAsFileSystemHandle() is conditionally polyfilled
import 'native-file-system-adapter'
window.ondrop = async evt => {
evt.preventDefault()
for (let item of evt.dataTransfer.items) {
item.getAsFileSystemHandle(handle => {
console.log(handle)
})
const handle = await item.getAsFileSystemHandle()
console.log(handle)
}
}
```
### Copy file handles to sandboxed file system
```js
import { showOpenFilePicker, getOriginPrivateDirectory } from 'native-file-system-adapter'
// request user to select a file
const fileHandle = await showOpenFilePicker({
const [fileHandle] = await showOpenFilePicker({
types: [], // default

@@ -104,6 +140,12 @@ multiple: false, // default

const fileHandle = await rootHandle.getFileHandle(file.name, { create: true })
await fileHandle.write(file)
fileHandle.close()
const writable = await fileHandle.createWritable()
await writable.write(file)
await writable.close()
```
// save/download a file
### Save / download a file
```js
import { showSaveFilePicker } from 'native-file-system-adapter'
const fileHandle = await showSaveFilePicker({

@@ -129,20 +171,33 @@ _preferPolyfill: false,

await blob.stream().pipeTo(fileHandle.getWriter())
await blob.stream().pipeTo(fileHandle.createWritable())
// or
var writer = fileHandle.getWriter()
writer.write(blob)
writer.close()
var writer = fileHandle.getWritable()
await writer.write(blob)
await writer.close()
```
PS: storing a file handle in IndexedDB or sharing it with postMessage isn't currently possible unless you use native.
Will get to it at some point in the feature
## Supported browsers
### A note when downloading with the polyfilled version
When importing as an ES module, browsers that support [dynamic imports](https://caniuse.com/es6-module-dynamic-import) and ES2018 features are a minimum requirement. When using a bundler, this restriction is no longer applicable.
Saving/downloading a file borrowing some of ideas from [StreamSaver.js](https://github.com/jimmywarting/StreamSaver.js).
When the directory picker falls back to `input` elements, the browser must support [webkitdirectory](https://caniuse.com/mdn-api_htmlinputelement_webkitdirectory) and [webkitRelativePath](https://caniuse.com/mdn-api_file_webkitrelativepath). Because of this, support for picking directories is generally poor on Mobile browsers.
For drag and drop, the `getAsFileSystemHandle()` polyfill depends on the `File and Directory Entries API` support, more specifically [FileSystemDirectoryEntry](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryEntry), [FileSystemFileEntry](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileEntry) and [webkitGetAsEntry](https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry).
## Limitations
- Storing a file handle in IndexedDB or sharing it with postMessage isn't currently possible unless you use native.
- `showDirectoryPicker` and `showOpenFilePicker` will not throw any `AbortError`s (e.g. user cancellations) when using a fallback input element
- `showSaveFilePicker` may not actually show any prompt when using a fallback with `<a download>`
- Cache adapter only works in secure (HTTPS) contexts `window.isSecureContext === true`
- IndexedDB adapter may not work in some browsers in Private mode
## A note when downloading with the polyfilled version
Saving/downloading a file is borrowing some of ideas from [StreamSaver.js](https://github.com/jimmywarting/StreamSaver.js).
The difference is:
- Using service worker is optional choice with this adapter.
- It dose not rely on some man-in-the-middle or external hosted service worker.
- It does not rely on some man-in-the-middle or external hosted service worker.
- 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)
- You don't have to handle web-streams-polyfills it's lazy loaded when needed when you need that writable stream. 😴

@@ -159,16 +214,18 @@ to set up a service worker you have to basically copy [the example](https://github.com/jimmywarting/native-file-system-adapter/tree/master/example/sw.js) and register it:

### Testing
## Testing
- start up a server and open `/examples/test.html` in your browser.
- for node: `npm i && npm run test`
- For browser tests: In project folder, run `php -S localhost:3000` or `npx http-server -p 3000 .`
- open `http://localhost:3000/example/test.html` in your browser.
- For node: `npm run test-node`
- For deno: `npm run test-deno`
### Resources
## Resources
I recommend to follow up on this links for you to learn more about the API and how it works
- https://web.dev/native-file-system/
- https://wicg.github.io/native-file-system/
- https://github.com/wicg/native-file-system
- https://web.dev/file-system-access
- https://wicg.github.io/file-system-access
- https://github.com/WICG/file-system-access
### Alternatives
## Alternatives
- [browser-fs-access](https://github.com/GoogleChromeLabs/browser-fs-access) by [@tomayac](https://github.com/tomayac): A similar, more like a shim (without `getSystemDirectory`).

@@ -178,4 +235,4 @@ - [StreamSaver](https://github.com/jimmywarting/StreamSaver.js) by [@jimmywarting](https://github.com/jimmywarting): A way to save large data to the disk directly with a writable stream <br><small>(same technique can be achieved if service worker are setup properly)</small>

### License
## License
native-file-system-adapter is licensed under the MIT License. See `LICENSE` for details.

@@ -1,4 +0,2 @@

// @ts-check
import { join, basename } from 'https://deno.land/std@0.98.0/path/mod.ts'
import { join, basename } from 'https://deno.land/std@0.108.0/path/mod.ts'
import { errors } from '../util.js'

@@ -5,0 +3,0 @@

@@ -27,3 +27,3 @@ /* global Blob, DOMException, Response, MessageChannel */

// @ts-ignore
const ponyfill = await import('https://cdn.jsdelivr.net/npm/web-streams-polyfill@2.1.1/dist/ponyfill.es2018.mjs')
const ponyfill = await import('https://cdn.jsdelivr.net/npm/web-streams-polyfill@3/dist/ponyfill.es2018.mjs')
TransformStream = ponyfill.TransformStream

@@ -30,0 +30,0 @@ WritableStream = ponyfill.WritableStream

@@ -1,8 +0,7 @@

import fs from 'fs/promises'
import { errors } from '../util.js'
import { join } from 'path'
import fs from 'node:fs/promises'
import { join } from 'node:path'
import 'node-domexception'
import Blob from 'fetch-blob'
import { fileFrom } from 'fetch-blob/from.js'
import DOMException from 'node-domexception'
import { errors } from '../util.js'
// import mime from 'mime-types'

@@ -9,0 +8,0 @@

@@ -19,16 +19,30 @@ const kAdapter = Symbol('adapter')

async queryPermission (options = {}) {
if (options.readable) return 'granted'
async queryPermission ({mode = 'read'} = {}) {
const handle = this[kAdapter]
return handle.queryPermission ?
await handle.queryPermission(options) :
handle.writable
? 'granted'
: 'denied'
if (handle.queryPermission) {
return handle.queryPermission({mode})
}
if (mode === 'read') {
return 'granted'
} else if (mode === 'readwrite') {
return handle.writable ? 'granted' : 'denied'
} else {
throw new TypeError(`Mode ${mode} must be 'read' or 'readwrite'`)
}
}
async requestPermission (options = {}) {
if (options.readable) return 'granted'
async requestPermission ({mode = 'read'} = {}) {
const handle = this[kAdapter]
return handle.writable ? 'granted' : 'denied'
if (handle.requestPermission) {
return handle.requestPermission({mode})
}
if (mode === 'read') {
return 'granted'
} else if (mode === 'readwrite') {
return handle.writable ? 'granted' : 'denied'
} else {
throw new TypeError(`Mode ${mode} must be 'read' or 'readwrite'`)
}
}

@@ -51,3 +65,8 @@

if (this === other) return true
if (this.kind !== other.kind) return false
if (
(!other) ||
(typeof other !== 'object') ||
(this.kind !== other.kind) ||
(!other[kAdapter])
) return false
return this[kAdapter].isSameEntry(other[kAdapter])

@@ -54,0 +73,0 @@ }

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc