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 3.0.0 to 3.0.1

types/src/config.d.ts

8

example/sw.js

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

// Want to remove this postMessage hack, tell them u want transferable streams:
// https://bugs.webkit.org/show_bug.cgi?id=215485
const WRITE = 0

@@ -27,2 +30,6 @@ const PULL = 0

pull () {
this.port.postMessage({ type: PULL })
}
/** @param {Error} reason */

@@ -41,3 +48,2 @@ cancel (reason) {

this.controller.enqueue(message.chunk)
this.port.postMessage({ type: PULL })
}

@@ -44,0 +50,0 @@ if (message.type === ABORT) {

4

package.json
{
"name": "native-file-system-adapter",
"version": "3.0.0",
"version": "3.0.1",
"description": "Native File System API",

@@ -27,3 +27,3 @@ "main": "src/es6.js",

},
"types": "/types/mod.d.ts",
"types": "types/mod.d.ts",
"keywords": [

@@ -30,0 +30,0 @@ "filesystem",

@@ -1,3 +0,1 @@

/* global Blob, DOMException, Response, MessageChannel */
import { errors } from '../util.js'

@@ -14,4 +12,4 @@ import config from '../config.js'

const { GONE } = errors
// @ts-ignore
const isSafari = /constructor/i.test(window.HTMLElement) || window.safari || window.WebKitPoint
// @ts-ignore - Don't match newer versions of Safari, but that's okay
const isOldSafari = /constructor/i.test(window.HTMLElement)

@@ -43,3 +41,3 @@ export class FileHandle {

if (isSafari || !sw) {
if (isOldSafari || !sw) {
/** @type {Blob[]} */

@@ -100,2 +98,5 @@ let chunks = []

// Want to remove this postMessage hack, tell them u want transferable streams:
// https://bugs.webkit.org/show_bug.cgi?id=215485
const WRITE = 0

@@ -102,0 +103,0 @@ const PULL = 0

@@ -5,4 +5,13 @@ /* global indexedDB, Blob, File, DOMException */

const { INVALID, GONE, MISMATCH, MOD_ERR, SYNTAX } = errors
const { INVALID, GONE, MISMATCH, MOD_ERR, SYNTAX, ABORT } = errors
/**
* @param {IDBTransaction} tx
* @param {(e) => {}} onerror
*/
function setupTxErrorHandler (tx, onerror) {
tx.onerror = () => onerror(tx.error)
tx.onabort = () => onerror(tx.error || new DOMException(...ABORT))
}
class Sink {

@@ -9,0 +18,0 @@ /**

@@ -5,6 +5,7 @@ import showDirectoryPicker from './showDirectoryPicker.js'

import getOriginPrivateDirectory from './getOriginPrivateDirectory.js'
// FileSystemWritableFileStream must be loaded before FileSystemFileHandle
import FileSystemWritableFileStream from './FileSystemWritableFileStream.js'
import FileSystemDirectoryHandle from './FileSystemDirectoryHandle.js'
import FileSystemFileHandle from './FileSystemFileHandle.js'
import FileSystemHandle from './FileSystemHandle.js'
import FileSystemWritableFileStream from './FileSystemWritableFileStream.js'

@@ -11,0 +12,0 @@ export {

import FileSystemHandle from './FileSystemHandle.js'
import { errors } from './util.js'
const { GONE, MOD_ERR } = errors
const kAdapter = Symbol('adapter')

@@ -93,2 +96,3 @@

let { handle: current, path } = openSet.pop()
for await (const entry of current.values()) {

@@ -136,3 +140,60 @@ if (await entry.isSameEntry(possibleDescendant)) {

if (globalThis.FileSystemDirectoryHandle) {
const proto = globalThis.FileSystemDirectoryHandle.prototype
proto.resolve = async function resolve (possibleDescendant) {
if (await possibleDescendant.isSameEntry(this)) {
return []
}
const openSet = [{ handle: this, path: [] }]
while (openSet.length) {
let { handle: current, path } = openSet.pop()
for await (const entry of current.values()) {
if (await entry.isSameEntry(possibleDescendant)) {
return [...path, entry.name]
}
if (entry.kind === 'directory') {
openSet.push({ handle: entry, path: [...path, entry.name] })
}
}
}
return null
}
// Safari allows us operate on deleted files,
// so we need to check if they still exist.
// Hope to remove this one day.
async function ensureDoActuallyStillExist (handle) {
const root = await navigator.storage.getDirectory()
const path = await root.resolve(handle)
if (path === null) { throw new DOMException(...GONE) }
}
const entries = proto.entries
proto.entries = async function * () {
await ensureDoActuallyStillExist(this)
yield * entries.call(this)
}
proto[Symbol.asyncIterator] = async function * () {
yield * this.entries()
}
const removeEntry = proto.removeEntry
proto.removeEntry = async function (name, options = {}) {
return removeEntry.call(this, name, options).catch(async err => {
const unknown = err instanceof DOMException && err.name === 'UnknownError'
if (unknown && !options.recursive) {
const empty = (await entries.call(this).next()).done
if (!empty) { throw new DOMException(...MOD_ERR) }
}
throw err
})
}
}
export default FileSystemDirectoryHandle
export { FileSystemDirectoryHandle }
import FileSystemHandle from './FileSystemHandle.js'
import FileSystemWritableFileStream from './FileSystemWritableFileStream.js'
import { errors } from './util.js'
const { INVALID, SYNTAX, GONE } = errors
const kAdapter = Symbol('adapter')

@@ -46,3 +49,182 @@

// Safari doesn't support async createWritable streams yet.
if (
globalThis.FileSystemFileHandle &&
!globalThis.FileSystemFileHandle.prototype.createWritable
) {
const wm = new WeakMap()
let workerUrl
// Worker code that should be inlined (can't use any external functions)
const code = () => {
let fileHandle, handle
onmessage = async evt => {
const port = evt.ports[0]
const cmd = evt.data
switch (cmd.type) {
case 'open':
const file = cmd.name
let dir = await navigator.storage.getDirectory()
for (const folder of cmd.path) {
dir = await dir.getDirectoryHandle(folder)
}
fileHandle = await dir.getFileHandle(file)
handle = await fileHandle.createSyncAccessHandle()
break
case 'write':
handle.write(cmd.data, { at: cmd.position })
handle.flush()
break
case 'truncate':
handle.truncate(cmd.size)
break
case 'abort':
case 'close':
handle.close()
break
}
port.postMessage(0)
}
}
globalThis.FileSystemFileHandle.prototype.createWritable = async function (options) {
// Safari only support writing data in a worker with sync access handle.
if (!workerUrl) {
const stringCode = `(${code.toString()})()`
const blob = new Blob([stringCode], {
type: 'text/javascript'
})
workerUrl = URL.createObjectURL(blob)
}
const worker = new Worker(workerUrl, { type: 'module' })
let position = 0
const textEncoder = new TextEncoder()
let size = await this.getFile().then(file => file.size)
const send = message => new Promise((resolve, reject) => {
const mc = new MessageChannel()
mc.port1.onmessage = evt => {
if (evt.data instanceof Error) reject(evt.data)
else resolve(evt.data)
mc.port1.close()
mc.port2.close()
mc.port1.onmessage = null
}
worker.postMessage(message, [mc.port2])
})
// Safari also don't support transferable file system handles.
// So we need to pass the path to the worker. This is a bit hacky and ugly.
const root = await navigator.storage.getDirectory()
const parent = await wm.get(this)
const path = await root.resolve(parent)
// Should likely never happen, but just in case...
if (path === null) throw new DOMException(...GONE)
let controller
await send({ type: 'open', path, name: this.name })
if (options?.keepExistingData === false) {
await send({ type: 'truncate', size: 0 })
size = 0
}
const ws = new FileSystemWritableFileStream({
start: ctrl => {
controller = ctrl
},
async write(chunk) {
const isPlainObject = chunk?.constructor === Object
if (isPlainObject) {
chunk = { ...chunk }
} else {
chunk = { type: 'write', data: chunk, position }
}
if (chunk.type === 'write') {
if (!('data' in chunk)) {
await send({ type: 'close' })
throw new DOMException(...SYNTAX('write requires a data argument'))
}
chunk.position ??= position
if (typeof chunk.data === 'string') {
chunk.data = textEncoder.encode(chunk.data)
}
else if (chunk.data instanceof ArrayBuffer) {
chunk.data = new Uint8Array(chunk.data)
}
else if (!(chunk.data instanceof Uint8Array) && ArrayBuffer.isView(chunk.data)) {
chunk.data = new Uint8Array(chunk.data.buffer, chunk.data.byteOffset, chunk.data.byteLength)
}
else if (!(chunk.data instanceof Uint8Array)) {
const ab = await new Response(chunk.data).arrayBuffer()
chunk.data = new Uint8Array(ab)
}
if (Number.isInteger(chunk.position) && chunk.position >= 0) {
position = chunk.position
}
position += chunk.data.byteLength
size += chunk.data.byteLength
} else if (chunk.type === 'seek') {
if (Number.isInteger(chunk.position) && chunk.position >= 0) {
if (size < chunk.position) {
throw new DOMException(...INVALID)
}
console.log('seeking', chunk)
position = chunk.position
return // Don't need to enqueue seek...
} else {
await send({ type: 'close' })
throw new DOMException(...SYNTAX('seek requires a position argument'))
}
} else if (chunk.type === 'truncate') {
if (Number.isInteger(chunk.size) && chunk.size >= 0) {
size = chunk.size
if (position > size) { position = size }
} else {
await send({ type: 'close' })
throw new DOMException(...SYNTAX('truncate requires a size argument'))
}
}
await send(chunk)
},
async close () {
await send({ type: 'close' })
worker.terminate()
},
async abort (reason) {
await send({ type: 'abort', reason })
worker.terminate()
},
})
return ws
}
const orig = FileSystemDirectoryHandle.prototype.getFileHandle
FileSystemDirectoryHandle.prototype.getFileHandle = async function (...args) {
const handle = await orig.call(this, ...args)
wm.set(handle, this)
return handle
}
}
export default FileSystemFileHandle
export { FileSystemFileHandle }
const kAdapter = Symbol('adapter')
/**
* @typedef {Object} FileSystemHandlePermissionDescriptor
* @property {('read'|'readwrite')} [mode='read']
*/
class FileSystemHandle {

@@ -19,3 +23,5 @@ /** @type {FileSystemHandle} */

async queryPermission ({mode = 'read'} = {}) {
/** @param {FileSystemHandlePermissionDescriptor} descriptor */
async queryPermission (descriptor = {}) {
const { mode = 'read' } = descriptor
const handle = this[kAdapter]

@@ -83,3 +89,10 @@

// Safari safari doesn't support writable streams yet.
if (globalThis.FileSystemHandle) {
globalThis.FileSystemHandle.prototype.queryPermission ??= function (descriptor) {
return 'granted'
}
}
export default FileSystemHandle
export { FileSystemHandle }

@@ -6,5 +6,6 @@ import config from './config.js'

class FileSystemWritableFileStream extends WritableStream {
constructor (...args) {
super(...args)
#writer
constructor (writer) {
super(writer)
this.#writer = writer
// Stupid Safari hack to extend native classes

@@ -18,3 +19,3 @@ // https://bugs.webkit.org/show_bug.cgi?id=226201

close () {
async close () {
this._closed = true

@@ -38,2 +39,3 @@ const w = this.getWriter()

// The write(data) method steps are:
write (data) {

@@ -44,6 +46,13 @@ if (this._closed) {

// 1. Let writer be the result of getting a writer for this.
const writer = this.getWriter()
const p = writer.write(data)
// 2. Let result be the result of writing a chunk to writer given data.
const result = writer.write(data)
// 3. Release writer.
writer.releaseLock()
return p
// 4. Return result.
return result
}

@@ -66,3 +75,12 @@ }

// Safari safari doesn't support writable streams yet.
if (
globalThis.FileSystemFileHandle &&
!globalThis.FileSystemFileHandle.prototype.createWritable &&
!globalThis.FileSystemWritableFileStream
) {
globalThis.FileSystemWritableFileStream = FileSystemWritableFileStream
}
export default FileSystemWritableFileStream
export { FileSystemWritableFileStream }

@@ -17,8 +17,6 @@ /** @typedef {import('./FileSystemDirectoryHandle.js').default} FileSystemDirectoryHandle */

input.type = 'file'
input.webkitdirectory = true
// Fallback to multiple files input for iOS Safari
input.multiple = true
// Even with this check, the browser may support the attribute, but not the functionality (e.g. iOS Safari)
if (!('webkitdirectory' in input)) {
throw new Error(`HTMLInputElement.webkitdirectory is not supported`)
}
// See https://stackoverflow.com/questions/47664777/javascript-file-input-onchange-not-working-ios-safari-only

@@ -30,4 +28,2 @@ input.style.position = 'fixed'

input.webkitdirectory = true
// Lazy load while the user is choosing the directory

@@ -34,0 +30,0 @@ const p = import('./util.js')

@@ -33,5 +33,8 @@ /** @typedef {import('./FileSystemFileHandle.js').default} FileSystemFileHandle */

// See https://stackoverflow.com/questions/47664777/javascript-file-input-onchange-not-working-ios-safari-only
input.style.position = 'fixed'
input.style.top = '-100000px'
input.style.left = '-100000px'
Object.assign(input.style, {
position: 'fixed',
top: '-100000px',
left: '-100000px'
})
document.body.appendChild(input)

@@ -43,5 +46,6 @@

await new Promise(resolve => {
input.addEventListener('change', resolve)
input.addEventListener('change', resolve, { once: true })
input.click()
})
input.remove()

@@ -48,0 +52,0 @@ return p.then(m => m.getFileHandlesFromInput(input))

@@ -8,3 +8,3 @@ /** @typedef {import('./FileSystemFileHandle.js').default} FileSystemFileHandle */

* @param {boolean} [options.excludeAcceptAllOption=false] Prevent user for selecting any
* @param {Object[]} [options.accepts] Files you want to accept
* @param {Object[]} [options.types] Files you want to accept
* @param {string} [options.suggestedName] the name to fall back to when using polyfill

@@ -11,0 +11,0 @@ * @param {string} [options._name] the name to fall back to when using polyfill

@@ -46,2 +46,1 @@ export class Sink {

export default _default;
declare const File_1: typeof window.File;

@@ -5,3 +5,3 @@ export default showSaveFilePicker;

excludeAcceptAllOption?: boolean;
accepts?: any[];
types?: any[];
suggestedName?: string;

@@ -8,0 +8,0 @@ _name?: string;

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