* Small explanation about the filesystem support

This commit is contained in:
Michael Van Canneyt 2024-05-18 23:30:52 +02:00
parent 013f0bbdf5
commit d8bea5b79a
10 changed files with 15956 additions and 0 deletions

View File

@ -0,0 +1,17 @@
# WebAssembly Filesystem demo
The filesystem demo relies on ZenFS:
https://zen-fs.github.io/
Included here are 2 files from ZenFS:
browser.min.js (ZenFS core)
browser.dom.js (ZenFS DOM backends)
They must be included in any pas2js program if you wish to enable filesystem
support for WebAssembly.

View File

@ -0,0 +1,864 @@
"use strict";
var ZenFS_DOM = (() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
IndexedDB: () => IndexedDB,
IndexedDBStore: () => IndexedDBStore,
IndexedDBTransaction: () => IndexedDBTransaction,
WebAccess: () => WebAccess,
WebAccessFS: () => WebAccessFS,
WebStorage: () => WebStorage,
WebStorageStore: () => WebStorageStore
});
// global-externals:@zenfs/core
var core_default = ZenFS;
var { ActionType, Async, AsyncIndexFS, AsyncTransaction, BigIntStats, BigIntStatsFs, Dir, Dirent, Errno, ErrnoError, Fetch, FetchFS, File, FileIndex, FileSystem, FileType, InMemory, InMemoryStore, IndexDirInode, IndexFS, IndexFileInode, IndexInode, Inode, LockedFS, Mutex, NoSyncFile, Overlay, OverlayFS, Port, PortFS, PortFile, PreloadFile, ReadStream, Readonly, SimpleAsyncStore, SimpleTransaction, Stats, StatsCommon, StatsFs, StoreFS, Sync, SyncIndexFS, SyncTransaction, Transaction, UnlockedOverlayFS, WriteStream, _toUnixTimestamp, access, accessSync, appendFile, appendFileSync, attachFS, checkOptions, chmod, chmodSync, chown, chownSync, close, closeSync, configure, constants, copyFile, copyFileSync, cp, cpSync, createReadStream, createWriteStream, decode, decodeDirListing, detachFS, encode, encodeDirListing, errorMessages, exists, existsSync, fchmod, fchmodSync, fchown, fchownSync, fdatasync, fdatasyncSync, flagToMode, flagToNumber, flagToString, fs, fstat, fstatSync, fsync, fsyncSync, ftruncate, ftruncateSync, futimes, futimesSync, isAppendable, isBackend, isBackendConfig, isExclusive, isReadable, isSynchronous, isTruncating, isWriteable, lchmod, lchmodSync, lchown, lchownSync, levenshtein, link, linkSync, lopenSync, lstat, lstatSync, lutimes, lutimesSync, mkdir, mkdirSync, mkdirpSync, mkdtemp, mkdtempSync, mount, mountObject, mounts, nop, normalizeMode, normalizeOptions, normalizePath, normalizeTime, open, openAsBlob, openSync, opendir, opendirSync, parseFlag, pathExistsAction, pathNotExistsAction, promises, randomIno, read, readFile, readFileSync, readSync, readdir, readdirSync, readlink, readlinkSync, readv, readvSync, realpath, realpathSync, rename, renameSync, resolveMountConfig, rm, rmSync, rmdir, rmdirSync, rootCred, rootIno, setImmediate, size_max, stat, statSync, statfs, statfsSync, symlink, symlinkSync, truncate, truncateSync, umount, unlink, unlinkSync, unwatchFile, utimes, utimesSync, watch, watchFile, write, writeFile, writeFileSync, writeSync, writev, writevSync } = ZenFS;
// node_modules/@zenfs/core/dist/emulation/path.js
function normalizeString(path, allowAboveRoot) {
let res = "";
let lastSegmentLength = 0;
let lastSlash = -1;
let dots = 0;
let char = "\0";
for (let i = 0; i <= path.length; ++i) {
if (i < path.length) {
char = path[i];
} else if (char == "/") {
break;
} else {
char = "/";
}
if (char == "/") {
if (lastSlash === i - 1 || dots === 1) {
} else if (dots === 2) {
if (res.length < 2 || lastSegmentLength !== 2 || res.at(-1) !== "." || res.at(-2) !== ".") {
if (res.length > 2) {
const lastSlashIndex = res.lastIndexOf("/");
if (lastSlashIndex === -1) {
res = "";
lastSegmentLength = 0;
} else {
res = res.slice(0, lastSlashIndex);
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
}
lastSlash = i;
dots = 0;
continue;
} else if (res.length !== 0) {
res = "";
lastSegmentLength = 0;
lastSlash = i;
dots = 0;
continue;
}
}
if (allowAboveRoot) {
res += res.length > 0 ? "/.." : "..";
lastSegmentLength = 2;
}
} else {
if (res.length > 0)
res += "/" + path.slice(lastSlash + 1, i);
else
res = path.slice(lastSlash + 1, i);
lastSegmentLength = i - lastSlash - 1;
}
lastSlash = i;
dots = 0;
} else if (char === "." && dots !== -1) {
++dots;
} else {
dots = -1;
}
}
return res;
}
__name(normalizeString, "normalizeString");
function normalize(path) {
if (!path.length)
return ".";
const isAbsolute = path.startsWith("/");
const trailingSeparator = path.endsWith("/");
path = normalizeString(path, !isAbsolute);
if (!path.length) {
if (isAbsolute)
return "/";
return trailingSeparator ? "./" : ".";
}
if (trailingSeparator)
path += "/";
return isAbsolute ? `/${path}` : path;
}
__name(normalize, "normalize");
function join(...parts) {
if (!parts.length)
return ".";
const joined = parts.join("/");
if (!joined?.length)
return ".";
return normalize(joined);
}
__name(join, "join");
function dirname(path) {
if (path.length === 0)
return ".";
const hasRoot = path[0] === "/";
let end = -1;
let matchedSlash = true;
for (let i = path.length - 1; i >= 1; --i) {
if (path[i] === "/") {
if (!matchedSlash) {
end = i;
break;
}
} else {
matchedSlash = false;
}
}
if (end === -1)
return hasRoot ? "/" : ".";
if (hasRoot && end === 1)
return "//";
return path.slice(0, end);
}
__name(dirname, "dirname");
function basename(path, suffix) {
let start = 0;
let end = -1;
let matchedSlash = true;
if (suffix !== void 0 && suffix.length > 0 && suffix.length <= path.length) {
if (suffix === path)
return "";
let extIdx = suffix.length - 1;
let firstNonSlashEnd = -1;
for (let i = path.length - 1; i >= 0; --i) {
if (path[i] === "/") {
if (!matchedSlash) {
start = i + 1;
break;
}
} else {
if (firstNonSlashEnd === -1) {
matchedSlash = false;
firstNonSlashEnd = i + 1;
}
if (extIdx >= 0) {
if (path[i] === suffix[extIdx]) {
if (--extIdx === -1) {
end = i;
}
} else {
extIdx = -1;
end = firstNonSlashEnd;
}
}
}
}
if (start === end)
end = firstNonSlashEnd;
else if (end === -1)
end = path.length;
return path.slice(start, end);
}
for (let i = path.length - 1; i >= 0; --i) {
if (path[i] === "/") {
if (!matchedSlash) {
start = i + 1;
break;
}
} else if (end === -1) {
matchedSlash = false;
end = i + 1;
}
}
if (end === -1)
return "";
return path.slice(start, end);
}
__name(basename, "basename");
// src/utils.ts
function errnoForDOMException(ex) {
switch (ex.name) {
case "IndexSizeError":
case "HierarchyRequestError":
case "InvalidCharacterError":
case "InvalidStateError":
case "SyntaxError":
case "NamespaceError":
case "TypeMismatchError":
case "ConstraintError":
case "VersionError":
case "URLMismatchError":
case "InvalidNodeTypeError":
return "EINVAL";
case "WrongDocumentError":
return "EXDEV";
case "NoModificationAllowedError":
case "InvalidModificationError":
case "InvalidAccessError":
case "SecurityError":
case "NotAllowedError":
return "EACCES";
case "NotFoundError":
return "ENOENT";
case "NotSupportedError":
return "ENOTSUP";
case "InUseAttributeError":
return "EBUSY";
case "NetworkError":
return "ENETDOWN";
case "AbortError":
return "EINTR";
case "QuotaExceededError":
return "ENOSPC";
case "TimeoutError":
return "ETIMEDOUT";
case "ReadOnlyError":
return "EROFS";
case "DataCloneError":
case "EncodingError":
case "NotReadableError":
case "DataError":
case "TransactionInactiveError":
case "OperationError":
case "UnknownError":
default:
return "EIO";
}
}
__name(errnoForDOMException, "errnoForDOMException");
function convertException(ex, path, syscall) {
if (ex instanceof ErrnoError) {
return ex;
}
const code = ex instanceof DOMException ? Errno[errnoForDOMException(ex)] : Errno.EIO;
const error = new ErrnoError(code, ex.message, path, syscall);
error.stack = ex.stack;
error.cause = ex.cause;
return error;
}
__name(convertException, "convertException");
// src/access.ts
var WebAccessFS = class extends Async(FileSystem) {
_handles = /* @__PURE__ */ new Map();
/**
* @hidden
*/
_sync;
constructor({ handle }) {
super();
this._handles.set("/", handle);
this._sync = InMemory.create({ name: "accessfs-cache" });
}
metadata() {
return {
...super.metadata(),
name: "WebAccess"
};
}
async sync(path, data, stats) {
const currentStats = await this.stat(path);
if (stats.mtime !== currentStats.mtime) {
await this.writeFile(path, data);
}
}
async rename(oldPath, newPath) {
try {
const handle = await this.getHandle(oldPath);
if (handle instanceof FileSystemDirectoryHandle) {
const files = await this.readdir(oldPath);
await this.mkdir(newPath);
if (files.length == 0) {
await this.unlink(oldPath);
} else {
for (const file of files) {
await this.rename(join(oldPath, file), join(newPath, file));
await this.unlink(oldPath);
}
}
}
if (!(handle instanceof FileSystemFileHandle)) {
return;
}
const oldFile = await handle.getFile(), destFolder = await this.getHandle(dirname(newPath));
if (!(destFolder instanceof FileSystemDirectoryHandle)) {
return;
}
const newFile = await destFolder.getFileHandle(basename(newPath), { create: true });
const writable = await newFile.createWritable();
await writable.write(await oldFile.arrayBuffer());
writable.close();
await this.unlink(oldPath);
} catch (ex) {
throw convertException(ex, oldPath, "rename");
}
}
async writeFile(fname, data) {
const handle = await this.getHandle(dirname(fname));
if (!(handle instanceof FileSystemDirectoryHandle)) {
return;
}
const file = await handle.getFileHandle(basename(fname), { create: true });
const writable = await file.createWritable();
await writable.write(data);
await writable.close();
}
async createFile(path, flag) {
await this.writeFile(path, new Uint8Array());
return this.openFile(path, flag);
}
async stat(path) {
const handle = await this.getHandle(path);
if (!handle) {
throw ErrnoError.With("ENOENT", path, "stat");
}
if (handle instanceof FileSystemDirectoryHandle) {
return new Stats({ mode: 511 | FileType.DIRECTORY, size: 4096 });
}
if (handle instanceof FileSystemFileHandle) {
const { lastModified, size } = await handle.getFile();
return new Stats({ mode: 511 | FileType.FILE, size, mtimeMs: lastModified });
}
throw new ErrnoError(Errno.EBADE, "Handle is not a directory or file", path, "stat");
}
async openFile(path, flag) {
const handle = await this.getHandle(path);
if (!(handle instanceof FileSystemFileHandle)) {
throw ErrnoError.With("EISDIR", path, "openFile");
}
try {
const file = await handle.getFile();
const data = new Uint8Array(await file.arrayBuffer());
const stats = new Stats({ mode: 511 | FileType.FILE, size: file.size, mtimeMs: file.lastModified });
return new PreloadFile(this, path, flag, stats, data);
} catch (ex) {
throw convertException(ex, path, "openFile");
}
}
async unlink(path) {
const handle = await this.getHandle(dirname(path));
if (handle instanceof FileSystemDirectoryHandle) {
try {
await handle.removeEntry(basename(path), { recursive: true });
} catch (ex) {
throw convertException(ex, path, "unlink");
}
}
}
async link(srcpath) {
throw ErrnoError.With("ENOSYS", srcpath, "WebAccessFS.link");
}
async rmdir(path) {
return this.unlink(path);
}
async mkdir(path) {
const existingHandle = await this.getHandle(path);
if (existingHandle) {
throw ErrnoError.With("EEXIST", path, "mkdir");
}
const handle = await this.getHandle(dirname(path));
if (!(handle instanceof FileSystemDirectoryHandle)) {
throw ErrnoError.With("ENOTDIR", path, "mkdir");
}
await handle.getDirectoryHandle(basename(path), { create: true });
}
async readdir(path) {
const handle = await this.getHandle(path);
if (!(handle instanceof FileSystemDirectoryHandle)) {
throw ErrnoError.With("ENOTDIR", path, "readdir");
}
const _keys = [];
for await (const key of handle.keys()) {
_keys.push(join(path, key));
}
return _keys;
}
async getHandle(path) {
if (this._handles.has(path)) {
return this._handles.get(path);
}
let walked = "/";
for (const part of path.split("/").slice(1)) {
const handle = this._handles.get(walked);
if (!(handle instanceof FileSystemDirectoryHandle)) {
throw ErrnoError.With("ENOTDIR", walked, "getHandle");
}
walked = join(walked, part);
try {
const dirHandle = await handle.getDirectoryHandle(part);
this._handles.set(walked, dirHandle);
} catch (_ex) {
const ex = _ex;
if (ex.name == "TypeMismatchError") {
try {
const fileHandle = await handle.getFileHandle(part);
this._handles.set(walked, fileHandle);
} catch (ex2) {
convertException(ex2, walked, "getHandle");
}
}
if (ex.name === "TypeError") {
throw new ErrnoError(Errno.ENOENT, ex.message, walked, "getHandle");
}
convertException(ex, walked, "getHandle");
}
}
return this._handles.get(path);
}
};
__name(WebAccessFS, "WebAccessFS");
var WebAccess = {
name: "WebAccess",
options: {
handle: {
type: "object",
required: true,
description: "The directory handle to use for the root"
}
},
isAvailable() {
return typeof FileSystemHandle == "function";
},
create(options) {
return new WebAccessFS(options);
}
};
// node_modules/@zenfs/core/dist/error.js
var Errno2;
(function(Errno3) {
Errno3[Errno3["EPERM"] = 1] = "EPERM";
Errno3[Errno3["ENOENT"] = 2] = "ENOENT";
Errno3[Errno3["EINTR"] = 4] = "EINTR";
Errno3[Errno3["EIO"] = 5] = "EIO";
Errno3[Errno3["ENXIO"] = 6] = "ENXIO";
Errno3[Errno3["EBADF"] = 9] = "EBADF";
Errno3[Errno3["EAGAIN"] = 11] = "EAGAIN";
Errno3[Errno3["ENOMEM"] = 12] = "ENOMEM";
Errno3[Errno3["EACCES"] = 13] = "EACCES";
Errno3[Errno3["EFAULT"] = 14] = "EFAULT";
Errno3[Errno3["ENOTBLK"] = 15] = "ENOTBLK";
Errno3[Errno3["EBUSY"] = 16] = "EBUSY";
Errno3[Errno3["EEXIST"] = 17] = "EEXIST";
Errno3[Errno3["EXDEV"] = 18] = "EXDEV";
Errno3[Errno3["ENODEV"] = 19] = "ENODEV";
Errno3[Errno3["ENOTDIR"] = 20] = "ENOTDIR";
Errno3[Errno3["EISDIR"] = 21] = "EISDIR";
Errno3[Errno3["EINVAL"] = 22] = "EINVAL";
Errno3[Errno3["ENFILE"] = 23] = "ENFILE";
Errno3[Errno3["EMFILE"] = 24] = "EMFILE";
Errno3[Errno3["ETXTBSY"] = 26] = "ETXTBSY";
Errno3[Errno3["EFBIG"] = 27] = "EFBIG";
Errno3[Errno3["ENOSPC"] = 28] = "ENOSPC";
Errno3[Errno3["ESPIPE"] = 29] = "ESPIPE";
Errno3[Errno3["EROFS"] = 30] = "EROFS";
Errno3[Errno3["EMLINK"] = 31] = "EMLINK";
Errno3[Errno3["EPIPE"] = 32] = "EPIPE";
Errno3[Errno3["EDOM"] = 33] = "EDOM";
Errno3[Errno3["ERANGE"] = 34] = "ERANGE";
Errno3[Errno3["EDEADLK"] = 35] = "EDEADLK";
Errno3[Errno3["ENAMETOOLONG"] = 36] = "ENAMETOOLONG";
Errno3[Errno3["ENOLCK"] = 37] = "ENOLCK";
Errno3[Errno3["ENOSYS"] = 38] = "ENOSYS";
Errno3[Errno3["ENOTEMPTY"] = 39] = "ENOTEMPTY";
Errno3[Errno3["ELOOP"] = 40] = "ELOOP";
Errno3[Errno3["ENOMSG"] = 42] = "ENOMSG";
Errno3[Errno3["EBADE"] = 52] = "EBADE";
Errno3[Errno3["EBADR"] = 53] = "EBADR";
Errno3[Errno3["EXFULL"] = 54] = "EXFULL";
Errno3[Errno3["ENOANO"] = 55] = "ENOANO";
Errno3[Errno3["EBADRQC"] = 56] = "EBADRQC";
Errno3[Errno3["ENOSTR"] = 60] = "ENOSTR";
Errno3[Errno3["ENODATA"] = 61] = "ENODATA";
Errno3[Errno3["ETIME"] = 62] = "ETIME";
Errno3[Errno3["ENOSR"] = 63] = "ENOSR";
Errno3[Errno3["ENONET"] = 64] = "ENONET";
Errno3[Errno3["EREMOTE"] = 66] = "EREMOTE";
Errno3[Errno3["ENOLINK"] = 67] = "ENOLINK";
Errno3[Errno3["ECOMM"] = 70] = "ECOMM";
Errno3[Errno3["EPROTO"] = 71] = "EPROTO";
Errno3[Errno3["EBADMSG"] = 74] = "EBADMSG";
Errno3[Errno3["EOVERFLOW"] = 75] = "EOVERFLOW";
Errno3[Errno3["EBADFD"] = 77] = "EBADFD";
Errno3[Errno3["ESTRPIPE"] = 86] = "ESTRPIPE";
Errno3[Errno3["ENOTSOCK"] = 88] = "ENOTSOCK";
Errno3[Errno3["EDESTADDRREQ"] = 89] = "EDESTADDRREQ";
Errno3[Errno3["EMSGSIZE"] = 90] = "EMSGSIZE";
Errno3[Errno3["EPROTOTYPE"] = 91] = "EPROTOTYPE";
Errno3[Errno3["ENOPROTOOPT"] = 92] = "ENOPROTOOPT";
Errno3[Errno3["EPROTONOSUPPORT"] = 93] = "EPROTONOSUPPORT";
Errno3[Errno3["ESOCKTNOSUPPORT"] = 94] = "ESOCKTNOSUPPORT";
Errno3[Errno3["ENOTSUP"] = 95] = "ENOTSUP";
Errno3[Errno3["ENETDOWN"] = 100] = "ENETDOWN";
Errno3[Errno3["ENETUNREACH"] = 101] = "ENETUNREACH";
Errno3[Errno3["ENETRESET"] = 102] = "ENETRESET";
Errno3[Errno3["ETIMEDOUT"] = 110] = "ETIMEDOUT";
Errno3[Errno3["ECONNREFUSED"] = 111] = "ECONNREFUSED";
Errno3[Errno3["EHOSTDOWN"] = 112] = "EHOSTDOWN";
Errno3[Errno3["EHOSTUNREACH"] = 113] = "EHOSTUNREACH";
Errno3[Errno3["EALREADY"] = 114] = "EALREADY";
Errno3[Errno3["EINPROGRESS"] = 115] = "EINPROGRESS";
Errno3[Errno3["ESTALE"] = 116] = "ESTALE";
Errno3[Errno3["EREMOTEIO"] = 121] = "EREMOTEIO";
Errno3[Errno3["EDQUOT"] = 122] = "EDQUOT";
})(Errno2 || (Errno2 = {}));
var errorMessages2 = {
[Errno2.EPERM]: "Operation not permitted",
[Errno2.ENOENT]: "No such file or directory",
[Errno2.EINTR]: "Interrupted system call",
[Errno2.EIO]: "Input/output error",
[Errno2.ENXIO]: "No such device or address",
[Errno2.EBADF]: "Bad file descriptor",
[Errno2.EAGAIN]: "Resource temporarily unavailable",
[Errno2.ENOMEM]: "Cannot allocate memory",
[Errno2.EACCES]: "Permission denied",
[Errno2.EFAULT]: "Bad address",
[Errno2.ENOTBLK]: "Block device required",
[Errno2.EBUSY]: "Resource busy or locked",
[Errno2.EEXIST]: "File exists",
[Errno2.EXDEV]: "Invalid cross-device link",
[Errno2.ENODEV]: "No such device",
[Errno2.ENOTDIR]: "File is not a directory",
[Errno2.EISDIR]: "File is a directory",
[Errno2.EINVAL]: "Invalid argument",
[Errno2.ENFILE]: "Too many open files in system",
[Errno2.EMFILE]: "Too many open files",
[Errno2.ETXTBSY]: "Text file busy",
[Errno2.EFBIG]: "File is too big",
[Errno2.ENOSPC]: "No space left on disk",
[Errno2.ESPIPE]: "Illegal seek",
[Errno2.EROFS]: "Cannot modify a read-only file system",
[Errno2.EMLINK]: "Too many links",
[Errno2.EPIPE]: "Broken pipe",
[Errno2.EDOM]: "Numerical argument out of domain",
[Errno2.ERANGE]: "Numerical result out of range",
[Errno2.EDEADLK]: "Resource deadlock would occur",
[Errno2.ENAMETOOLONG]: "File name too long",
[Errno2.ENOLCK]: "No locks available",
[Errno2.ENOSYS]: "Function not implemented",
[Errno2.ENOTEMPTY]: "Directory is not empty",
[Errno2.ELOOP]: "Too many levels of symbolic links",
[Errno2.ENOMSG]: "No message of desired type",
[Errno2.EBADE]: "Invalid exchange",
[Errno2.EBADR]: "Invalid request descriptor",
[Errno2.EXFULL]: "Exchange full",
[Errno2.ENOANO]: "No anode",
[Errno2.EBADRQC]: "Invalid request code",
[Errno2.ENOSTR]: "Device not a stream",
[Errno2.ENODATA]: "No data available",
[Errno2.ETIME]: "Timer expired",
[Errno2.ENOSR]: "Out of streams resources",
[Errno2.ENONET]: "Machine is not on the network",
[Errno2.EREMOTE]: "Object is remote",
[Errno2.ENOLINK]: "Link has been severed",
[Errno2.ECOMM]: "Communication error on send",
[Errno2.EPROTO]: "Protocol error",
[Errno2.EBADMSG]: "Bad message",
[Errno2.EOVERFLOW]: "Value too large for defined data type",
[Errno2.EBADFD]: "File descriptor in bad state",
[Errno2.ESTRPIPE]: "Streams pipe error",
[Errno2.ENOTSOCK]: "Socket operation on non-socket",
[Errno2.EDESTADDRREQ]: "Destination address required",
[Errno2.EMSGSIZE]: "Message too long",
[Errno2.EPROTOTYPE]: "Protocol wrong type for socket",
[Errno2.ENOPROTOOPT]: "Protocol not available",
[Errno2.EPROTONOSUPPORT]: "Protocol not supported",
[Errno2.ESOCKTNOSUPPORT]: "Socket type not supported",
[Errno2.ENOTSUP]: "Operation is not supported",
[Errno2.ENETDOWN]: "Network is down",
[Errno2.ENETUNREACH]: "Network is unreachable",
[Errno2.ENETRESET]: "Network dropped connection on reset",
[Errno2.ETIMEDOUT]: "Connection timed out",
[Errno2.ECONNREFUSED]: "Connection refused",
[Errno2.EHOSTDOWN]: "Host is down",
[Errno2.EHOSTUNREACH]: "No route to host",
[Errno2.EALREADY]: "Operation already in progress",
[Errno2.EINPROGRESS]: "Operation now in progress",
[Errno2.ESTALE]: "Stale file handle",
[Errno2.EREMOTEIO]: "Remote I/O error",
[Errno2.EDQUOT]: "Disk quota exceeded"
};
var ErrnoError2 = class extends Error {
static fromJSON(json) {
const err = new ErrnoError2(json.errno, json.message, json.path, json.syscall);
err.code = json.code;
err.stack = json.stack;
return err;
}
static With(code, path, syscall) {
return new ErrnoError2(Errno2[code], errorMessages2[Errno2[code]], path, syscall);
}
/**
* Represents a ZenFS error. Passed back to applications after a failed
* call to the ZenFS API.
*
* Error codes mirror those returned by regular Unix file operations, which is
* what Node returns.
* @param type The type of the error.
* @param message A descriptive error message.
*/
constructor(errno, message = errorMessages2[errno], path, syscall = "") {
super(message);
this.errno = errno;
this.path = path;
this.syscall = syscall;
this.code = Errno2[errno];
this.message = `${this.code}: ${message}${this.path ? `, '${this.path}'` : ""}`;
}
/**
* @return A friendly error message.
*/
toString() {
return this.message;
}
toJSON() {
return {
errno: this.errno,
code: this.code,
path: this.path,
stack: this.stack,
message: this.message,
syscall: this.syscall
};
}
/**
* The size of the API error in buffer-form in bytes.
*/
bufferSize() {
return 4 + JSON.stringify(this.toJSON()).length;
}
};
__name(ErrnoError2, "ErrnoError");
// node_modules/@zenfs/core/dist/backends/store/store.js
var Transaction2 = class {
constructor() {
this.aborted = false;
}
async [Symbol.asyncDispose]() {
if (this.aborted) {
return;
}
await this.commit();
}
[Symbol.dispose]() {
if (this.aborted) {
return;
}
this.commitSync();
}
};
__name(Transaction2, "Transaction");
var AsyncTransaction2 = class extends Transaction2 {
getSync(ino) {
throw ErrnoError2.With("ENOSYS", void 0, "AsyncTransaction.getSync");
}
setSync(ino, data) {
throw ErrnoError2.With("ENOSYS", void 0, "AsyncTransaction.setSync");
}
removeSync(ino) {
throw ErrnoError2.With("ENOSYS", void 0, "AsyncTransaction.removeSync");
}
commitSync() {
throw ErrnoError2.With("ENOSYS", void 0, "AsyncTransaction.commitSync");
}
abortSync() {
throw ErrnoError2.With("ENOSYS", void 0, "AsyncTransaction.abortSync");
}
};
__name(AsyncTransaction2, "AsyncTransaction");
// src/IndexedDB.ts
function wrap(request) {
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = (e) => {
e.preventDefault();
reject(convertException(request.error));
};
});
}
__name(wrap, "wrap");
var IndexedDBTransaction = class extends AsyncTransaction2 {
constructor(tx, store) {
super();
this.tx = tx;
this.store = store;
}
get(key) {
return wrap(this.store.get(key.toString()));
}
async set(key, data) {
await wrap(this.store.put(data, key.toString()));
}
remove(key) {
return wrap(this.store.delete(key.toString()));
}
async commit() {
this.tx.commit();
}
async abort() {
try {
this.tx.abort();
} catch (e) {
throw convertException(e);
}
}
};
__name(IndexedDBTransaction, "IndexedDBTransaction");
async function createDB(name, indexedDB = globalThis.indexedDB) {
const req = indexedDB.open(name);
req.onupgradeneeded = () => {
const db = req.result;
if (db.objectStoreNames.contains(name)) {
db.deleteObjectStore(name);
}
db.createObjectStore(name);
};
const result = await wrap(req);
return result;
}
__name(createDB, "createDB");
var IndexedDBStore = class {
constructor(db) {
this.db = db;
}
sync() {
throw new Error("Method not implemented.");
}
get name() {
return IndexedDB.name + ":" + this.db.name;
}
clear() {
return wrap(this.db.transaction(this.db.name, "readwrite").objectStore(this.db.name).clear());
}
clearSync() {
throw ErrnoError.With("ENOSYS", void 0, "IndexedDBStore.clearSync");
}
transaction() {
const tx = this.db.transaction(this.db.name, "readwrite");
return new IndexedDBTransaction(tx, tx.objectStore(this.db.name));
}
};
__name(IndexedDBStore, "IndexedDBStore");
var IndexedDB = {
name: "IndexedDB",
options: {
storeName: {
type: "string",
required: false,
description: "The name of this file system. You can have multiple IndexedDB file systems operating at once, but each must have a different name."
},
idbFactory: {
type: "object",
required: false,
description: "The IDBFactory to use. Defaults to globalThis.indexedDB."
}
},
async isAvailable(idbFactory = globalThis.indexedDB) {
try {
if (!(idbFactory instanceof IDBFactory)) {
return false;
}
const req = idbFactory.open("__zenfs_test");
await wrap(req);
idbFactory.deleteDatabase("__zenfs_test");
return true;
} catch (e) {
idbFactory.deleteDatabase("__zenfs_test");
return false;
}
},
async create(options) {
const db = await createDB(options.storeName || "zenfs", options.idbFactory);
const store = new IndexedDBStore(db);
const fs2 = new StoreFS(store);
return fs2;
}
};
// src/Storage.ts
var WebStorageStore = class {
constructor(_storage) {
this._storage = _storage;
}
get name() {
return WebStorage.name;
}
clear() {
this._storage.clear();
}
clearSync() {
this._storage.clear();
}
async sync() {
}
transaction() {
return new SimpleTransaction(this);
}
get(key) {
const data = this._storage.getItem(key.toString());
if (typeof data != "string") {
return;
}
return encode(data);
}
set(key, data) {
try {
this._storage.setItem(key.toString(), decode(data));
} catch (e) {
throw new ErrnoError(Errno.ENOSPC, "Storage is full.");
}
}
delete(key) {
try {
this._storage.removeItem(key.toString());
} catch (e) {
throw new ErrnoError(Errno.EIO, "Unable to delete key " + key + ": " + e);
}
}
};
__name(WebStorageStore, "WebStorageStore");
var WebStorage = {
name: "WebStorage",
options: {
storage: {
type: "object",
required: false,
description: "The Storage to use. Defaults to globalThis.localStorage."
}
},
isAvailable(storage = globalThis.localStorage) {
return storage instanceof globalThis.Storage;
},
create({ storage = globalThis.localStorage }) {
return new StoreFS(new WebStorageStore(storage));
}
};
return __toCommonJS(src_exports);
})();
//# sourceMappingURL=browser.js.map

14734
demo/wasienv/filesystem/browser.min.js vendored Normal file

File diff suppressed because it is too large Load Diff

1
demo/wasienv/filesystem/bulma.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="12"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<Title Value="filesystemhost"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<CustomData Count="6">
<Item0 Name="BrowserConsole" Value="1"/>
<Item1 Name="MaintainHTML" Value="1"/>
<Item2 Name="Pas2JSProject" Value="1"/>
<Item3 Name="PasJSLocation" Value="$NameOnly($(ProjFile))"/>
<Item4 Name="PasJSWebBrowserProject" Value="1"/>
<Item5 Name="RunAtReady" Value="1"/>
</CustomData>
<BuildModes>
<Item Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
</RunParams>
<Units>
<Unit>
<Filename Value="filesystemhost.lpr"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="index.html"/>
<IsPartOfProject Value="True"/>
<CustomData Count="1">
<Item0 Name="PasJSIsProjectHTMLFile" Value="1"/>
</CustomData>
</Unit>
<Unit>
<Filename Value="wasitypes.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="wasizenfs.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target FileExt=".js">
<Filename Value="filesystemhost"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="js"/>
</SearchPaths>
<Parsing>
<SyntaxOptions>
<AllowLabel Value="False"/>
<UseAnsiStrings Value="False"/>
<CPPInline Value="False"/>
</SyntaxOptions>
</Parsing>
<CodeGeneration>
<TargetOS Value="browser"/>
</CodeGeneration>
<Linking>
<Debugging>
<GenerateDebugInfo Value="False"/>
<UseLineInfoUnit Value="False"/>
</Debugging>
</Linking>
<Other>
<CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc -Jminclude"/>
<CompilerPath Value="$(pas2js)"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions>
<Item>
<Name Value="EAbort"/>
</Item>
<Item>
<Name Value="ECodetoolError"/>
</Item>
<Item>
<Name Value="EFOpenError"/>
</Item>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,50 @@
program filesystemhost;
{$mode objfpc}
uses
BrowserConsole, BrowserApp, WASIHostApp, JS, Classes, SysUtils, Web, wasitypes, wasizenfs, libzenfs, libzenfsdom;
type
TMyApplication = class(TWASIHostApplication)
protected
FS :TWASIZenFS;
procedure RunWasm ; async;
procedure DoRun; override;
public
end;
procedure TMyApplication.DoRun;
begin
RunWasm;
end;
procedure TMyApplication.RunWasm;
begin
// Writeln('Enabling logging');
// WasiEnvironment.LogAPI:=True;
await(tjsobject, ZenFS.configure(
new(
['mounts', new([
'/', DomBackends.WebStorage
])
])
)
);
FS:=TWASIZenFS.Create;
WasiEnvironment.FS:=FS;
StartWebAssembly('fsdemo.wasm');
end;
var
Application : TMyApplication;
begin
ConsoleStyle:=DefaultCRTConsoleStyle;
HookConsole;
Application:=TMyApplication.Create(nil);
Application.Initialize;
Application.Run;
end.

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="12"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<Title Value="fsdemo"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<BuildModes>
<Item Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
</RunParams>
<Units>
<Unit>
<Filename Value="fsdemo.lpr"/>
<IsPartOfProject Value="True"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target>
<Filename Value="fsdemo"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Linking>
<Debugging>
<DebugInfoType Value="dsDwarf3"/>
</Debugging>
</Linking>
</CompilerOptions>
<Debugging>
<Exceptions>
<Item>
<Name Value="EAbort"/>
</Item>
<Item>
<Name Value="ECodetoolError"/>
</Item>
<Item>
<Name Value="EFOpenError"/>
</Item>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,73 @@
program fsdemo;
uses SysUtils;
Const
{$ifdef cpuwasm32}
OurDir = '/tmp';
{$else}
OurDir = '/tmp/something';
{$Endif}
OurFile = OurDir+'/test.txt';
var
HasDir : Boolean;
HasFile: Boolean;
S : UTF8String;
aSize,FD,byteCount : Integer;
begin
S:='Hello, WebAssembly World!';
HasDir:=DirectoryExists(OurDir);
if HasDir then
Writeln('Directory already exists: ',OurDir)
else if CreateDir(OurDir) then
Writeln('Created new directory: ',OurDir)
else
Writeln('Failed to create directory: ',OurDir);
HasFile:=FileExists(OurFile);
If HasFile then
Writeln('File exists: ',OurFile)
else
begin
Writeln('Creating file: ',OurFile);
FD:=FileCreate(OurFile);
if FD=-1 then
Writeln('Failed to get fileHandle: ',FD)
else
begin
Writeln('Got fileHandle: ',FD);
ByteCount:=FileWrite(FD,S[1],Length(S));
Writeln('Wrote ',byteCount,' bytes to file. Expected: ',Length(S));
FileClose(FD);
Writeln('Closed file');
end;
end;
If FileExists(OurFile) then
begin
Writeln('Opening file: ',OurFile);
FD:=FileOpen(OurFile,fmOpenRead);
if FD=-1 then
Writeln('Failed to get fileHandle: ',FD)
else
begin
Writeln('Got fileHandle: ',FD);
aSize:=FileSeek(FD,0,fsFromEnd);
Writeln('Got file size: ',aSize);
FileSeek(FD,0,fsFromBeginning);
SetLength(S,aSize);
if aSize>0 then
ByteCount:=FileRead(FD,S[1],aSize);
Writeln('Read ',byteCount,' bytes from file. Expected: ',aSize);
Writeln('File contents: "',S,'"');
FileClose(FD);
Writeln('Closed file');
end;
end;
if Not HasFile then
Writeln('Deleting file ',DeleteFile(OurFile));
if Not HasDir then
Writeln('Deleting directory ',RemoveDir(OurDir));
Writeln('All done!');
end.

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectGroup FileVersion="2">
<Targets>
<Target FileName="filesystemhost.lpi">
<BuildModes>
<Mode Name="Default"/>
</BuildModes>
</Target>
<Target FileName="fsdemo.lpi">
<BuildModes>
<Mode Name="Default"/>
</BuildModes>
</Target>
</Targets>
</ProjectGroup>
</CONFIG>

View File

@ -0,0 +1,40 @@
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>FPC-Webassembly and Pas2JS Filesystem Demo</title>
<link href="bulma.min.css" rel="stylesheet">
<script src="browser.min.js"></script>
<script src="browser.dom.js"></script>
<script src="filesystemhost.js"></script>
<style>
</style>
</head>
<body>
<div class="section pb-4">
<h1 class="title is-3">Webassembly filesystem support demo</h1>
<p>This demo demonstrates a webassembly program creating a directory and a file (if they do not exist yet) and read data from that file.</p>
<p>The filesystem is provided in the browser using <a href="https://zen-fs.github.io/">ZenFS</a>, the successor of BrowserFS.</p>
<p class="pb-2">The Pascal I/O and the WASI API are synchronous, so only a synchronous filesystem can be used.</p>
<h1 class="title is-5">Webassembly console output:</h1>
<div class="box" id="pasjsconsole"></div>
</div>
<!-- <hr> -->
<div class="section">
<div class="source">
<div class="source-inner">
<div>
<p>Created using &nbsp; <a target="_blank" href="https://wiki.freepascal.org/pas2js">pas2js.</a> </p>
<p>Pas2JS Sources: &nbsp; <a target="new" href="filesystemhost.lpr">Pas2JS loader Program</a></p>
<p>Webassembly Sources: &nbsp; <a target="new" href="fsdemo.pp">fsdemo.lpr</a></p>
</div>
</div>
</div>
</div>
<script>
rtl.showUncaughtExceptions=true;
rtl.run();
</script>
</body>
</html>