@zenfs/core 0.9.6 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/AsyncStore.d.ts +3 -2
- package/dist/backends/AsyncStore.js +40 -33
- package/dist/backends/Fetch.d.ts +84 -0
- package/dist/backends/Fetch.js +171 -0
- package/dist/backends/InMemory.d.ts +1 -1
- package/dist/backends/Index.d.ts +7 -10
- package/dist/backends/Index.js +26 -24
- package/dist/backends/Locked.d.ts +11 -11
- package/dist/backends/Locked.js +50 -49
- package/dist/backends/Overlay.js +22 -22
- package/dist/backends/SyncStore.d.ts +6 -6
- package/dist/backends/SyncStore.js +30 -30
- package/dist/backends/backend.d.ts +5 -4
- package/dist/backends/backend.js +6 -6
- package/dist/backends/port/fs.d.ts +124 -0
- package/dist/backends/port/fs.js +241 -0
- package/dist/backends/port/rpc.d.ts +60 -0
- package/dist/backends/port/rpc.js +71 -0
- package/dist/backends/port/store.d.ts +30 -0
- package/dist/backends/port/store.js +142 -0
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +4 -4
- package/dist/config.d.ts +9 -11
- package/dist/config.js +13 -13
- package/dist/emulation/async.d.ts +76 -77
- package/dist/emulation/async.js +45 -45
- package/dist/emulation/dir.js +8 -7
- package/dist/emulation/index.d.ts +1 -1
- package/dist/emulation/index.js +1 -1
- package/dist/emulation/path.d.ts +3 -2
- package/dist/emulation/path.js +19 -45
- package/dist/emulation/promises.d.ts +112 -113
- package/dist/emulation/promises.js +167 -173
- package/dist/emulation/shared.d.ts +6 -17
- package/dist/emulation/shared.js +9 -9
- package/dist/emulation/streams.js +3 -2
- package/dist/emulation/sync.d.ts +71 -64
- package/dist/emulation/sync.js +62 -63
- package/dist/{ApiError.d.ts → error.d.ts} +15 -15
- package/dist/error.js +292 -0
- package/dist/file.d.ts +6 -4
- package/dist/file.js +17 -9
- package/dist/filesystem.d.ts +1 -1
- package/dist/filesystem.js +18 -15
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/mutex.js +4 -3
- package/dist/stats.d.ts +7 -7
- package/dist/stats.js +50 -10
- package/dist/utils.d.ts +10 -9
- package/dist/utils.js +15 -15
- package/package.json +5 -5
- package/readme.md +19 -11
- package/src/backends/AsyncStore.ts +42 -36
- package/src/backends/Fetch.ts +230 -0
- package/src/backends/Index.ts +33 -29
- package/src/backends/Locked.ts +50 -49
- package/src/backends/Overlay.ts +24 -24
- package/src/backends/SyncStore.ts +34 -34
- package/src/backends/backend.ts +13 -11
- package/src/backends/port/fs.ts +308 -0
- package/src/backends/port/readme.md +59 -0
- package/src/backends/port/rpc.ts +144 -0
- package/src/backends/port/store.ts +187 -0
- package/src/config.ts +25 -29
- package/src/emulation/async.ts +191 -199
- package/src/emulation/dir.ts +8 -8
- package/src/emulation/index.ts +1 -1
- package/src/emulation/path.ts +25 -49
- package/src/emulation/promises.ts +286 -287
- package/src/emulation/shared.ts +14 -23
- package/src/emulation/streams.ts +9 -8
- package/src/emulation/sync.ts +182 -182
- package/src/{ApiError.ts → error.ts} +91 -89
- package/src/file.ts +23 -13
- package/src/filesystem.ts +26 -22
- package/src/index.ts +4 -1
- package/src/mutex.ts +6 -4
- package/src/stats.ts +32 -23
- package/src/utils.ts +23 -24
- package/tsconfig.json +4 -3
- package/dist/ApiError.js +0 -292
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
3
|
+
import type { TransferListItem } from 'worker_threads';
|
|
4
|
+
import { type PortFS } from './fs.js';
|
|
5
|
+
type _MessageEvent<T = any> = T | {
|
|
6
|
+
data: T;
|
|
7
|
+
};
|
|
8
|
+
export interface Port {
|
|
9
|
+
postMessage(value: unknown, transferList?: ReadonlyArray<TransferListItem>): void;
|
|
10
|
+
on?(event: 'message', listener: (value: unknown) => void): this;
|
|
11
|
+
off?(event: 'message', listener: (value: unknown) => void): this;
|
|
12
|
+
addEventListener?(type: 'message', listener: (this: Port, ev: _MessageEvent) => void): void;
|
|
13
|
+
removeEventListener?(type: 'message', listener: (this: Port, ev: _MessageEvent) => void): void;
|
|
14
|
+
}
|
|
15
|
+
export interface Options {
|
|
16
|
+
/**
|
|
17
|
+
* The target port that you want to connect to, or the current port if in a port context.
|
|
18
|
+
*/
|
|
19
|
+
port: Port;
|
|
20
|
+
/**
|
|
21
|
+
* How long to wait for a request to complete
|
|
22
|
+
*/
|
|
23
|
+
timeout?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* An RPC message
|
|
27
|
+
*/
|
|
28
|
+
export interface Message<TScope extends string = string, TMethod extends string = string> {
|
|
29
|
+
_zenfs: true;
|
|
30
|
+
scope: TScope;
|
|
31
|
+
id: string;
|
|
32
|
+
method: TMethod;
|
|
33
|
+
stack: string;
|
|
34
|
+
}
|
|
35
|
+
export interface Request<TScope extends string = string, TMethod extends string = string, TArgs extends unknown[] = unknown[]> extends Message<TScope, TMethod> {
|
|
36
|
+
args: TArgs;
|
|
37
|
+
}
|
|
38
|
+
export interface Response<TScope extends string = string, TMethod extends string = string, TValue = unknown> extends Message<TScope, TMethod> {
|
|
39
|
+
error: boolean;
|
|
40
|
+
value: Awaited<TValue> extends File ? FileData : Awaited<TValue>;
|
|
41
|
+
}
|
|
42
|
+
export interface FileData {
|
|
43
|
+
fd: number;
|
|
44
|
+
path: string;
|
|
45
|
+
position: number;
|
|
46
|
+
}
|
|
47
|
+
export { FileData as File };
|
|
48
|
+
export declare function isMessage(arg: unknown): arg is Message<string, string>;
|
|
49
|
+
type _Executor = Parameters<ConstructorParameters<typeof Promise<any>>[0]>;
|
|
50
|
+
export interface Executor {
|
|
51
|
+
resolve: _Executor[0];
|
|
52
|
+
reject: _Executor[1];
|
|
53
|
+
fs?: PortFS;
|
|
54
|
+
}
|
|
55
|
+
export declare function request<const TRequest extends Request, TValue>(request: Omit<TRequest, 'id' | 'stack' | '_zenfs'>, { port, timeout, fs }?: Partial<Options> & {
|
|
56
|
+
fs?: PortFS;
|
|
57
|
+
}): Promise<TValue>;
|
|
58
|
+
export declare function handleResponse<const TResponse extends Response>(response: TResponse): void;
|
|
59
|
+
export declare function attach<T extends Message>(port: Port, handler: (message: T) => unknown): void;
|
|
60
|
+
export declare function detach<T extends Message>(port: Port, handler: (message: T) => unknown): void;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ErrnoError, Errno } from '../../error.js';
|
|
2
|
+
import { PortFile } from './fs.js';
|
|
3
|
+
function isFileData(value) {
|
|
4
|
+
return typeof value == 'object' && value != null && 'fd' in value && 'path' in value && 'position' in value;
|
|
5
|
+
}
|
|
6
|
+
// general types
|
|
7
|
+
export function isMessage(arg) {
|
|
8
|
+
return typeof arg == 'object' && arg != null && '_zenfs' in arg && !!arg._zenfs;
|
|
9
|
+
}
|
|
10
|
+
const executors = new Map();
|
|
11
|
+
export function request(request, { port, timeout = 1000, fs } = {}) {
|
|
12
|
+
const stack = new Error().stack.slice('Error:'.length);
|
|
13
|
+
if (!port) {
|
|
14
|
+
throw ErrnoError.With('EINVAL');
|
|
15
|
+
}
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const id = Math.random().toString(16).slice(10);
|
|
18
|
+
executors.set(id, { resolve, reject, fs });
|
|
19
|
+
port.postMessage({ ...request, _zenfs: true, id, stack });
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
const error = new ErrnoError(Errno.EIO, 'RPC Failed');
|
|
22
|
+
error.stack += stack;
|
|
23
|
+
reject(error);
|
|
24
|
+
}, timeout);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function handleResponse(response) {
|
|
28
|
+
if (!isMessage(response)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const { id, value, error, stack } = response;
|
|
32
|
+
if (!executors.has(id)) {
|
|
33
|
+
const error = new ErrnoError(Errno.EIO, 'Invalid RPC id:' + id);
|
|
34
|
+
error.stack += stack;
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
const { resolve, reject, fs } = executors.get(id);
|
|
38
|
+
if (error) {
|
|
39
|
+
const e = ErrnoError.fromJSON(value);
|
|
40
|
+
e.stack += stack;
|
|
41
|
+
reject(e);
|
|
42
|
+
executors.delete(id);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (isFileData(value)) {
|
|
46
|
+
const { fd, path, position } = value;
|
|
47
|
+
const file = new PortFile(fs, fd, path, position);
|
|
48
|
+
resolve(file);
|
|
49
|
+
executors.delete(id);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
resolve(value);
|
|
53
|
+
executors.delete(id);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
export function attach(port, handler) {
|
|
57
|
+
if (!port) {
|
|
58
|
+
throw ErrnoError.With('EINVAL');
|
|
59
|
+
}
|
|
60
|
+
port['on' in port ? 'on' : 'addEventListener']('message', (message) => {
|
|
61
|
+
handler('data' in message ? message.data : message);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
export function detach(port, handler) {
|
|
65
|
+
if (!port) {
|
|
66
|
+
throw ErrnoError.With('EINVAL');
|
|
67
|
+
}
|
|
68
|
+
port['off' in port ? 'off' : 'removeEventListener']('message', (message) => {
|
|
69
|
+
handler('data' in message ? message.data : message);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type AsyncStore, type AsyncTransaction } from '../AsyncStore.js';
|
|
2
|
+
import type { SyncStore } from '../SyncStore.js';
|
|
3
|
+
import type { Backend } from '../backend.js';
|
|
4
|
+
import * as RPC from './rpc.js';
|
|
5
|
+
import type { ExtractProperties } from 'utilium';
|
|
6
|
+
export declare class PortStore implements AsyncStore {
|
|
7
|
+
readonly options: RPC.Options;
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly port: RPC.Port;
|
|
10
|
+
constructor(options: RPC.Options, name?: string);
|
|
11
|
+
clear(): Promise<void>;
|
|
12
|
+
beginTransaction(): PortTransaction;
|
|
13
|
+
}
|
|
14
|
+
type TxMethods = ExtractProperties<AsyncTransaction, (...args: any[]) => Promise<any>>;
|
|
15
|
+
type TxMethod = keyof TxMethods;
|
|
16
|
+
export declare class PortTransaction implements AsyncTransaction {
|
|
17
|
+
readonly store: PortStore;
|
|
18
|
+
readonly id: number | Promise<number>;
|
|
19
|
+
constructor(store: PortStore, id: number | Promise<number>);
|
|
20
|
+
rpc<const T extends TxMethod>(method: T, ...args: Parameters<TxMethods[T]>): Promise<Awaited<ReturnType<TxMethods[T]>>>;
|
|
21
|
+
get(key: bigint): Promise<Uint8Array>;
|
|
22
|
+
put(key: bigint, data: Uint8Array, overwrite: boolean): Promise<boolean>;
|
|
23
|
+
remove(key: bigint): Promise<void>;
|
|
24
|
+
commit(): Promise<void>;
|
|
25
|
+
abort(): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
export declare function attachStore(port: RPC.Port, store: SyncStore | AsyncStore): void;
|
|
28
|
+
export declare function detachStore(port: RPC.Port, store: SyncStore | AsyncStore): void;
|
|
29
|
+
export declare const PortStoreBackend: Backend;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { ErrnoError, Errno } from '../../error.js';
|
|
3
|
+
import { AsyncStoreFS } from '../AsyncStore.js';
|
|
4
|
+
import * as RPC from './rpc.js';
|
|
5
|
+
export class PortStore {
|
|
6
|
+
constructor(options, name = 'port') {
|
|
7
|
+
this.options = options;
|
|
8
|
+
this.name = name;
|
|
9
|
+
this.port = options.port;
|
|
10
|
+
RPC.attach(this.port, RPC.handleResponse);
|
|
11
|
+
}
|
|
12
|
+
clear() {
|
|
13
|
+
return RPC.request({
|
|
14
|
+
scope: 'store',
|
|
15
|
+
method: 'clear',
|
|
16
|
+
args: [],
|
|
17
|
+
}, this.options);
|
|
18
|
+
}
|
|
19
|
+
beginTransaction() {
|
|
20
|
+
const id = RPC.request({
|
|
21
|
+
scope: 'store',
|
|
22
|
+
method: 'beginTransaction',
|
|
23
|
+
args: [],
|
|
24
|
+
}, this.options);
|
|
25
|
+
return new PortTransaction(this, id);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class PortTransaction {
|
|
29
|
+
constructor(store, id) {
|
|
30
|
+
this.store = store;
|
|
31
|
+
this.id = id;
|
|
32
|
+
}
|
|
33
|
+
async rpc(method, ...args) {
|
|
34
|
+
return RPC.request({
|
|
35
|
+
scope: 'transaction',
|
|
36
|
+
tx: await this.id,
|
|
37
|
+
method,
|
|
38
|
+
args,
|
|
39
|
+
}, this.store.options);
|
|
40
|
+
}
|
|
41
|
+
get(key) {
|
|
42
|
+
return this.rpc('get', key);
|
|
43
|
+
}
|
|
44
|
+
async put(key, data, overwrite) {
|
|
45
|
+
return await this.rpc('put', key, data, overwrite);
|
|
46
|
+
}
|
|
47
|
+
async remove(key) {
|
|
48
|
+
return await this.rpc('remove', key);
|
|
49
|
+
}
|
|
50
|
+
async commit() {
|
|
51
|
+
return await this.rpc('commit');
|
|
52
|
+
}
|
|
53
|
+
async abort() {
|
|
54
|
+
return await this.rpc('abort');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
let nextTx = 0;
|
|
58
|
+
const transactions = new Map();
|
|
59
|
+
async function handleRequest(port, store, request) {
|
|
60
|
+
if (!RPC.isMessage(request)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const { method, args, id, scope, stack } = request;
|
|
64
|
+
let value, error = false;
|
|
65
|
+
try {
|
|
66
|
+
switch (scope) {
|
|
67
|
+
case 'store':
|
|
68
|
+
// @ts-expect-error 2556
|
|
69
|
+
value = await store[method](...args);
|
|
70
|
+
if (method == 'beginTransaction') {
|
|
71
|
+
transactions.set(++nextTx, value);
|
|
72
|
+
value = nextTx;
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
case 'transaction':
|
|
76
|
+
const { tx } = request;
|
|
77
|
+
if (!transactions.has(tx)) {
|
|
78
|
+
throw new ErrnoError(Errno.EBADF);
|
|
79
|
+
}
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
81
|
+
// @ts-ignore 2556
|
|
82
|
+
value = await transactions.get(tx)[method](...args);
|
|
83
|
+
if (method == 'close') {
|
|
84
|
+
transactions.delete(tx);
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
default:
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
value = e;
|
|
93
|
+
error = true;
|
|
94
|
+
}
|
|
95
|
+
port.postMessage({
|
|
96
|
+
_zenfs: true,
|
|
97
|
+
scope,
|
|
98
|
+
id,
|
|
99
|
+
error,
|
|
100
|
+
method,
|
|
101
|
+
stack,
|
|
102
|
+
value: value instanceof ErrnoError ? value.toJSON() : value,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
export function attachStore(port, store) {
|
|
106
|
+
RPC.attach(port, (request) => handleRequest(port, store, request));
|
|
107
|
+
}
|
|
108
|
+
export function detachStore(port, store) {
|
|
109
|
+
RPC.detach(port, (request) => handleRequest(port, store, request));
|
|
110
|
+
}
|
|
111
|
+
export const PortStoreBackend = {
|
|
112
|
+
name: 'PortStore',
|
|
113
|
+
options: {
|
|
114
|
+
port: {
|
|
115
|
+
type: 'object',
|
|
116
|
+
description: 'The target port that you want to connect to',
|
|
117
|
+
validator(port) {
|
|
118
|
+
// Check for a `postMessage` function.
|
|
119
|
+
if (typeof port?.postMessage != 'function') {
|
|
120
|
+
throw new ErrnoError(Errno.EINVAL, 'option must be a port.');
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
async isAvailable() {
|
|
126
|
+
if ('WorkerGlobalScope' in globalThis && globalThis instanceof globalThis.WorkerGlobalScope) {
|
|
127
|
+
// Web Worker
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const worker_threads = await import('node:worker_threads');
|
|
132
|
+
// NodeJS worker
|
|
133
|
+
return 'Worker' in worker_threads;
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
create(options) {
|
|
140
|
+
return new AsyncStoreFS({ ...options, store: new PortStore(options, options?.name) });
|
|
141
|
+
},
|
|
142
|
+
};
|