-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
940 additions
and
259 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
This demo showcases `crudfs` and `casfs` interoperability in browser and Node.js. | ||
|
||
First, in browser an object is stored into `casfs` (Content Addressable Storage) | ||
and its hash (CID) is persisted using `crudfs`, in a real user folder. | ||
|
||
Then, from Node.js, the CID is retrieved using `crudfs` and the object is read | ||
using `casfs`. | ||
|
||
https://github.com/streamich/memfs/assets/9773803/02ba339c-6e13-4712-a02f-672674300d27 | ||
|
||
Run: | ||
|
||
``` | ||
yarn demo:git-fsa | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { FsaCrud } from '../../src/fsa-to-crud'; | ||
import { CrudCas } from '../../src/crud-to-cas'; | ||
import type * as fsa from '../../src/fsa/types'; | ||
|
||
const hash = async (data: Uint8Array): Promise<string> => { | ||
const hashBuffer = await crypto.subtle.digest('SHA-256', data); | ||
const hashArray = Array.from(new Uint8Array(hashBuffer)); | ||
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); | ||
return hashHex; | ||
}; | ||
|
||
const demo = async (dir: fsa.IFileSystemDirectoryHandle) => { | ||
try { | ||
const crud = new FsaCrud(dir); | ||
const cas = new CrudCas(await crud.from(['objects']), { hash }); | ||
|
||
// Store "Hello, world!" in object storage | ||
const cid = await cas.put(new TextEncoder().encode('Hello, world!')); | ||
|
||
// Store the CID in the refs/heads/main.txt file | ||
await crud.put(['refs', 'heads'], 'main.txt', new TextEncoder().encode(cid)); | ||
} catch (error) { | ||
console.log(error); | ||
console.log((<any>error).name); | ||
} | ||
}; | ||
|
||
const main = async () => { | ||
const button = document.createElement('button'); | ||
button.textContent = 'Select an empty folder'; | ||
document.body.appendChild(button); | ||
button.onclick = async () => { | ||
const dir = await (window as any).showDirectoryPicker({ id: 'demo', mode: 'readwrite' }); | ||
await demo(dir); | ||
}; | ||
}; | ||
|
||
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Run: npx ts-node demo/crud-and-cas/node.ts | ||
|
||
import { NodeCrud } from '../../src/node-to-crud'; | ||
import { CrudCas } from '../../src/crud-to-cas'; | ||
import * as fs from 'fs'; | ||
const root = require('app-root-path'); | ||
const path = require('path'); | ||
|
||
const hash = async (data: Uint8Array): Promise<string> => { | ||
const hashBuffer = await crypto.subtle.digest('SHA-256', data); | ||
const hashArray = Array.from(new Uint8Array(hashBuffer)); | ||
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); | ||
return hashHex; | ||
}; | ||
|
||
const main = async () => { | ||
const dir = path.resolve(root.path, 'fs-test'); | ||
const crud = new NodeCrud({ fs: <any>fs.promises, dir }); | ||
const cas = new CrudCas(await crud.from(['objects']), { hash }); | ||
|
||
// Retrieve the CID from the refs/heads/main.txt file | ||
const cid = await crud.get(['refs', 'heads'], 'main.txt'); | ||
const cidText = Buffer.from(cid).toString(); | ||
console.log('CID:', cidText); | ||
|
||
// Retrieve the data from the object storage | ||
const data = await cas.get(cidText); | ||
const dataText = Buffer.from(data).toString(); | ||
console.log('Content:', dataText); | ||
}; | ||
|
||
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
const path = require('path'); | ||
const HtmlWebpackPlugin = require('html-webpack-plugin'); | ||
const root = require('app-root-path'); | ||
|
||
module.exports = { | ||
mode: 'development', | ||
devtool: 'inline-source-map', | ||
entry: { | ||
bundle: __dirname + '/main', | ||
}, | ||
plugins: [ | ||
new HtmlWebpackPlugin({ | ||
title: 'Development', | ||
}), | ||
], | ||
module: { | ||
rules: [ | ||
{ | ||
test: /\.tsx?$/, | ||
exclude: /node_modules/, | ||
loader: 'ts-loader', | ||
}, | ||
], | ||
}, | ||
resolve: { | ||
extensions: ['.tsx', '.ts', '.js'], | ||
}, | ||
output: { | ||
filename: '[name].js', | ||
path: path.resolve(root.path, 'dist'), | ||
}, | ||
devServer: { | ||
port: 9876, | ||
hot: false, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Synchronous `fs` in browser | ||
|
||
This demo executes tests of **synchronous** Node.js API built on top of File | ||
System Access API in browser. | ||
|
||
There are two tests: | ||
|
||
- OPFS — virtual file system built into browsers. | ||
- Native File System Access API folder — a real folder that user explicitly grants permission to. | ||
|
||
See the assertions run in the browser console. | ||
|
||
https://github.com/streamich/memfs/assets/9773803/f841b6cc-728d-4341-b426-3daa6b43f8ac | ||
|
||
Run: | ||
|
||
``` | ||
demo:fsa-to-node-sync-tests | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
(window as any).process = require('process/browser'); | ||
(window as any).Buffer = require('buffer').Buffer; | ||
|
||
import { strictEqual, deepEqual } from 'assert'; | ||
|
||
import type * as fsa from '../../src/fsa/types'; | ||
import { FsaNodeFs, FsaNodeSyncAdapterWorker } from '../../src/fsa-to-node'; | ||
|
||
const demo = async (dir: fsa.IFileSystemDirectoryHandle) => { | ||
const adapter = await FsaNodeSyncAdapterWorker.start('https://localhost:9876/worker.js', dir); | ||
const fs = new FsaNodeFs(dir, adapter); | ||
|
||
await fs.promises.mkdir('/dir'); | ||
await fs.promises.writeFile('/test.txt', 'Hello world!'); | ||
const list = await fs.promises.readdir(''); | ||
console.log(list); | ||
|
||
console.log('existsSync()'); | ||
strictEqual(fs.existsSync('/test.txt'), true); | ||
|
||
console.log('statSync() - returns correct type for file'); | ||
strictEqual(fs.statSync('/test.txt').isFile(), true); | ||
strictEqual(fs.statSync('/test.txt').isDirectory(), false); | ||
|
||
console.log('statSync() - returns correct type for directory'); | ||
strictEqual(fs.statSync('/dir').isFile(), false); | ||
strictEqual(fs.statSync('/dir').isDirectory(), true); | ||
|
||
console.log('readFileSync() - can read file as text'); | ||
strictEqual(fs.readFileSync('/test.txt', 'utf8'), 'Hello world!'); | ||
|
||
console.log('writeFileSync() - can write text to a new file'); | ||
fs.writeFileSync('/cool.txt', 'worlds'); | ||
strictEqual(fs.readFileSync('/cool.txt', 'utf8'), 'worlds'); | ||
|
||
console.log('appendFileSync() - can append to an existing file'); | ||
fs.appendFileSync('/cool.txt', '!'); | ||
strictEqual(fs.readFileSync('/cool.txt', 'utf8'), 'worlds!'); | ||
|
||
console.log('copyFileSync() - can copy a file'); | ||
fs.copyFileSync('/cool.txt', '/cool (Copy).txt'); | ||
strictEqual(fs.readFileSync('/cool (Copy).txt', 'utf8'), 'worlds!'); | ||
|
||
console.log('renameSync() - can move a file'); | ||
fs.renameSync('/cool (Copy).txt', '/dir/very-cool.txt'); | ||
strictEqual(fs.readFileSync('/dir/very-cool.txt', 'utf8'), 'worlds!'); | ||
|
||
console.log('rmdirSync() - can remove an empty directory'); | ||
await fs.promises.mkdir('/to-be-deleted'); | ||
strictEqual(fs.existsSync('/to-be-deleted'), true); | ||
fs.rmdirSync('/to-be-deleted'); | ||
strictEqual(fs.existsSync('/to-be-deleted'), false); | ||
|
||
console.log('rmSync() - can delete a file'); | ||
await fs.promises.writeFile('/dir/tmp', '...'); | ||
strictEqual(fs.existsSync('/dir/tmp'), true); | ||
fs.rmSync('/dir/tmp'); | ||
strictEqual(fs.existsSync('/dir/tmp'), false); | ||
|
||
console.log('mkdirSync() - can create a nested directory'); | ||
fs.mkdirSync('/public/site/assets/img', { recursive: true }); | ||
strictEqual(fs.statSync('/public/site/assets/img').isDirectory(), true); | ||
|
||
console.log('mkdtempSync() - can create a temporary directory'); | ||
await fs.promises.mkdir('/tmp'); | ||
const tmpDirName = fs.mkdtempSync('/tmp/temporary-'); | ||
strictEqual(fs.statSync(tmpDirName).isDirectory(), true); | ||
|
||
console.log('truncateSync() - can truncate a file'); | ||
await fs.promises.writeFile('/truncated.txt', 'truncate here: abcdefghijklmnopqrstuvwxyz'); | ||
fs.truncateSync('/truncated.txt', 14); | ||
strictEqual(fs.readFileSync('/truncated.txt', 'utf8'), 'truncate here:'); | ||
|
||
console.log('unlinkSync() - can delete a file'); | ||
await fs.promises.writeFile('/delete-me.txt', 'abc'); | ||
fs.unlinkSync('/delete-me.txt'); | ||
strictEqual(fs.existsSync('/delete-me.txt'), false); | ||
|
||
console.log('readdirSync() - can list files in a directory'); | ||
const listInDir = fs.readdirSync('/dir'); | ||
deepEqual(listInDir, ['very-cool.txt']); | ||
|
||
console.log('readdirSync() - can list files in a directory as Dirent[]'); | ||
const listInDir2 = fs.readdirSync('/dir', { withFileTypes: true }) as any; | ||
deepEqual(listInDir2[0].name, 'very-cool.txt'); | ||
deepEqual(listInDir2[0].isFile(), true); | ||
|
||
console.log('readSync() - can read a file into a buffer'); | ||
const buf = Buffer.alloc(3); | ||
const readHandle = await fs.promises.open('/cool.txt', 'r'); | ||
const bytesRead = fs.readSync(readHandle.fd, buf, 0, 3, 0); | ||
strictEqual(bytesRead, 3); | ||
strictEqual(buf.toString('utf8'), 'wor'); | ||
|
||
console.log('writeSync() - can write into an open file'); | ||
const writeHandle = await fs.promises.open('/cool.txt', 'a'); | ||
const bytesWritten = fs.writeSync(writeHandle.fd, Buffer.from('W'), 0, 1, 0); | ||
await writeHandle.close(); | ||
strictEqual(bytesWritten, 1); | ||
strictEqual(fs.readFileSync('/cool.txt', 'utf8'), 'Worlds!'); | ||
|
||
console.log('openSync() - can create a file'); | ||
strictEqual(fs.existsSync('/new-file.txt'), false); | ||
const fd = fs.openSync('/new-file.txt', 'w'); | ||
strictEqual(fs.existsSync('/new-file.txt'), true); | ||
fs.unlinkSync('/new-file.txt'); | ||
strictEqual(typeof fd, 'number'); | ||
}; | ||
|
||
const main = async () => { | ||
const button = document.createElement('button'); | ||
button.textContent = 'Select an empty folder'; | ||
document.body.appendChild(button); | ||
button.onclick = async () => { | ||
const dir = await (window as any).showDirectoryPicker({ id: 'demo', mode: 'readwrite' }); | ||
await demo(dir); | ||
}; | ||
|
||
const button2 = document.createElement('button'); | ||
button2.textContent = 'Run tests in OPFS'; | ||
button2.style.marginLeft = '1em'; | ||
document.body.appendChild(button2); | ||
button2.onclick = async () => { | ||
const dir = await navigator.storage.getDirectory(); | ||
await demo(dir as any); | ||
}; | ||
}; | ||
|
||
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
const path = require('path'); | ||
const HtmlWebpackPlugin = require('html-webpack-plugin'); | ||
const root = require('app-root-path'); | ||
|
||
module.exports = { | ||
mode: 'development', | ||
devtool: 'inline-source-map', | ||
entry: { | ||
bundle: __dirname + '/main', | ||
worker: __dirname + '/worker', | ||
}, | ||
plugins: [ | ||
// new ForkTsCheckerWebpackPlugin(), | ||
new HtmlWebpackPlugin({ | ||
title: 'Development', | ||
}), | ||
], | ||
module: { | ||
rules: [ | ||
{ | ||
test: /\.tsx?$/, | ||
exclude: /node_modules/, | ||
loader: 'ts-loader', | ||
}, | ||
], | ||
}, | ||
resolve: { | ||
extensions: ['.tsx', '.ts', '.js'], | ||
fallback: { | ||
assert: require.resolve('assert'), | ||
buffer: require.resolve('buffer'), | ||
path: require.resolve('path-browserify'), | ||
process: require.resolve('process/browser'), | ||
// stream: require.resolve('streamx'), | ||
stream: require.resolve('readable-stream'), | ||
url: require.resolve('url'), | ||
util: require.resolve('util'), | ||
// fs: path.resolve(__dirname, '../../src/index.ts'), | ||
}, | ||
}, | ||
output: { | ||
filename: '[name].js', | ||
path: path.resolve(root.path, 'dist'), | ||
}, | ||
devServer: { | ||
// HTTPS is required for SharedArrayBuffer to work. | ||
https: true, | ||
headers: { | ||
// These two headers are required for SharedArrayBuffer to work. | ||
'Cross-Origin-Opener-Policy': 'same-origin', | ||
'Cross-Origin-Embedder-Policy': 'require-corp', | ||
}, | ||
port: 9876, | ||
hot: false, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
(self as any).process = require('process/browser'); | ||
(self as any).Buffer = require('buffer').Buffer; | ||
|
||
import { FsaNodeSyncWorker } from '../../src/fsa-to-node/worker/FsaNodeSyncWorker'; | ||
|
||
if (typeof window === 'undefined') { | ||
const worker = new FsaNodeSyncWorker(); | ||
worker.start(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
This demo shows how `tar-stream` package can be used to create a zip file from | ||
a folder selected with File System Access API. | ||
|
||
Below demo uses the File System Access API in the browser to get access to a real folder | ||
on the file system. It then converts a [`FileSystemDirectoryHandle`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle) instance | ||
to a Node-like `fs` file system. | ||
|
||
It then creates two text files using `fs.promises.writeFile`, and then uses `tar-stream` package to create a zip file | ||
and write it back got the file system using Node's `fs.createWriteStream`. | ||
|
||
https://github.com/streamich/memfs/assets/9773803/8dc61d1e-61bf-4dfc-973b-028332fd4473 | ||
|
||
Run: | ||
|
||
``` | ||
demo:fsa-to-node-zipfile | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
(window as any).process = require('process/browser'); | ||
(window as any).Buffer = require('buffer').Buffer; | ||
|
||
import type * as fsa from '../../src/fsa/types'; | ||
import { FsaNodeFs } from '../../src/fsa-to-node'; | ||
const tar = require('tar-stream'); | ||
|
||
const demo = async (dir: fsa.IFileSystemDirectoryHandle) => { | ||
const fs = new FsaNodeFs(dir); | ||
await fs.promises.writeFile('hello.txt', 'Hello World'); | ||
await fs.promises.writeFile('cool.txt', 'Cool Worlds channel'); | ||
|
||
const list = (await fs.promises.readdir('/')) as string[]; | ||
|
||
const pack = tar.pack(); | ||
const tarball = fs.createWriteStream('backups.tar'); | ||
pack.pipe(tarball); | ||
|
||
for (const item of list) { | ||
if (item[0] === '.') continue; | ||
const stat = await fs.promises.stat(item); | ||
if (!stat.isFile()) continue; | ||
pack.entry({ name: '/backups/' + item }, await fs.promises.readFile('/' + item), () => {}); | ||
} | ||
|
||
pack.finalize(); | ||
}; | ||
|
||
const main = async () => { | ||
const button = document.createElement('button'); | ||
button.textContent = 'Select an empty folder'; | ||
document.body.appendChild(button); | ||
button.onclick = async () => { | ||
const dir = await (window as any).showDirectoryPicker({ id: 'demo', mode: 'readwrite' }); | ||
await demo(dir); | ||
}; | ||
}; | ||
|
||
main(); |
Oops, something went wrong.