@pistonite/pure 0.28.0 → 0.29.1
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/LICENSE +1 -1
- package/README.md +23 -4
- package/dist/_dts_/src/log/index.d.ts +37 -0
- package/dist/_dts_/src/log/index.d.ts.map +1 -0
- package/dist/_dts_/src/log/logger.d.ts +27 -0
- package/dist/_dts_/src/log/logger.d.ts.map +1 -0
- package/dist/_dts_/src/memory/cell.d.ts +25 -0
- package/dist/_dts_/src/memory/cell.d.ts.map +1 -0
- package/dist/_dts_/src/memory/emp.d.ts +87 -0
- package/dist/_dts_/src/memory/emp.d.ts.map +1 -0
- package/dist/_dts_/src/memory/idgen.d.ts +18 -0
- package/dist/_dts_/src/memory/idgen.d.ts.map +1 -0
- package/dist/_dts_/src/memory/index.d.ts +10 -0
- package/dist/_dts_/src/memory/index.d.ts.map +1 -0
- package/dist/_dts_/src/memory/persist.d.ts +38 -0
- package/dist/_dts_/src/memory/persist.d.ts.map +1 -0
- package/dist/_dts_/src/result/index.d.ts +191 -0
- package/dist/_dts_/src/result/index.d.ts.map +1 -0
- package/dist/_dts_/src/sync/RwLock.d.ts +30 -0
- package/dist/_dts_/src/sync/RwLock.d.ts.map +1 -0
- package/dist/_dts_/src/sync/batch.d.ts +112 -0
- package/dist/_dts_/src/sync/batch.d.ts.map +1 -0
- package/dist/_dts_/src/sync/capture.d.ts +11 -0
- package/dist/_dts_/src/sync/capture.d.ts.map +1 -0
- package/dist/_dts_/src/sync/debounce.d.ts +105 -0
- package/dist/_dts_/src/sync/debounce.d.ts.map +1 -0
- package/dist/_dts_/src/sync/index.d.ts +15 -0
- package/dist/_dts_/src/sync/index.d.ts.map +1 -0
- package/dist/_dts_/src/sync/latest.d.ts +86 -0
- package/dist/_dts_/src/sync/latest.d.ts.map +1 -0
- package/dist/_dts_/src/sync/mutex.d.ts +14 -0
- package/dist/_dts_/src/sync/mutex.d.ts.map +1 -0
- package/dist/_dts_/src/sync/once.d.ts +84 -0
- package/dist/_dts_/src/sync/once.d.ts.map +1 -0
- package/dist/_dts_/src/sync/serial.d.ts +162 -0
- package/dist/_dts_/src/sync/serial.d.ts.map +1 -0
- package/dist/_dts_/src/sync/util.d.ts +19 -0
- package/dist/_dts_/src/sync/util.d.ts.map +1 -0
- package/dist/log/index.js +57 -0
- package/dist/log/index.js.map +1 -0
- package/dist/memory/index.js +92 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/result/index.js +29 -0
- package/dist/result/index.js.map +1 -0
- package/dist/sync/index.js +252 -0
- package/dist/sync/index.js.map +1 -0
- package/package.json +22 -13
- package/src/env.d.ts +1 -0
- package/src/log/index.ts +36 -11
- package/src/log/logger.ts +93 -115
- package/src/memory/cell.ts +21 -11
- package/src/memory/emp.ts +34 -23
- package/src/memory/idgen.test.ts +1 -1
- package/src/memory/index.ts +1 -4
- package/src/memory/persist.ts +9 -12
- package/src/result/index.ts +12 -4
- package/src/sync/batch.test.ts +1 -1
- package/src/sync/batch.ts +12 -17
- package/src/sync/capture.ts +2 -0
- package/src/sync/debounce.test.ts +1 -1
- package/src/sync/debounce.ts +12 -15
- package/src/sync/index.ts +2 -3
- package/src/sync/latest.test.ts +1 -1
- package/src/sync/latest.ts +19 -16
- package/src/sync/once.test.ts +1 -1
- package/src/sync/once.ts +13 -8
- package/src/sync/serial.test.ts +1 -1
- package/src/sync/serial.ts +14 -12
- package/src/sync/util.ts +2 -2
- package/src/fs/FsError.ts +0 -55
- package/src/fs/FsFile.ts +0 -67
- package/src/fs/FsFileImpl.ts +0 -219
- package/src/fs/FsFileMgr.ts +0 -29
- package/src/fs/FsFileStandalone.ts +0 -21
- package/src/fs/FsFileStandaloneImplFileAPI.ts +0 -54
- package/src/fs/FsFileStandaloneImplHandleAPI.ts +0 -147
- package/src/fs/FsFileSystem.ts +0 -71
- package/src/fs/FsFileSystemInternal.ts +0 -30
- package/src/fs/FsImplEntryAPI.ts +0 -149
- package/src/fs/FsImplFileAPI.ts +0 -116
- package/src/fs/FsImplHandleAPI.ts +0 -199
- package/src/fs/FsOpen.ts +0 -271
- package/src/fs/FsOpenFile.ts +0 -256
- package/src/fs/FsPath.ts +0 -137
- package/src/fs/FsSave.ts +0 -216
- package/src/fs/FsSupportStatus.ts +0 -87
- package/src/fs/index.ts +0 -123
- package/src/log/internal.ts +0 -14
- package/src/memory/async_erc.ts +0 -186
- package/src/memory/erc.test.ts +0 -258
- package/src/memory/erc.ts +0 -320
- package/src/pref/dark.ts +0 -151
- package/src/pref/device.ts +0 -118
- package/src/pref/index.ts +0 -13
- package/src/pref/inject_style.ts +0 -22
- package/src/pref/locale.ts +0 -296
package/src/sync/latest.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { type AnyFn, type AwaitRet, makePromise, type PromiseHandle } from "./util.ts";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Factory for `latest`. See {@link LatestConstructor} for usage.
|
|
5
|
+
*/
|
|
6
|
+
export function latest<TFn extends AnyFn>(args: LatestConstructor<TFn>) {
|
|
7
|
+
const { fn, areArgsEqual, updateArgs } = args;
|
|
8
|
+
const impl = new LatestImpl(fn, areArgsEqual, updateArgs);
|
|
9
|
+
return (...args: Parameters<TFn>) => impl.invoke(...args);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Args for constructing `latest`
|
|
14
|
+
*
|
|
15
|
+
* `latest` is an async event wrapper that always resolve to the result of the latest
|
|
5
16
|
* call
|
|
6
17
|
*
|
|
7
18
|
* ## Example
|
|
@@ -31,16 +42,7 @@ import { type AnyFn, type AwaitRet, makePromise, type PromiseHandle } from "./ut
|
|
|
31
42
|
* See the constructor options for more advanced usage, for example,
|
|
32
43
|
* control how arguments are updated when new calls are made.
|
|
33
44
|
*/
|
|
34
|
-
export
|
|
35
|
-
fn,
|
|
36
|
-
areArgsEqual,
|
|
37
|
-
updateArgs,
|
|
38
|
-
}: LatestConstructor<TFn>) {
|
|
39
|
-
const impl = new LatestImpl(fn, areArgsEqual, updateArgs);
|
|
40
|
-
return (...args: Parameters<TFn>) => impl.invoke(...args);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export type LatestConstructor<TFn extends AnyFn> = {
|
|
45
|
+
export interface LatestConstructor<TFn extends AnyFn> {
|
|
44
46
|
/** Function to be wrapped */
|
|
45
47
|
fn: TFn;
|
|
46
48
|
|
|
@@ -72,9 +74,10 @@ export type LatestConstructor<TFn extends AnyFn> = {
|
|
|
72
74
|
* to be executed next.
|
|
73
75
|
*
|
|
74
76
|
*/
|
|
75
|
-
updateArgs?:
|
|
76
|
-
}
|
|
77
|
-
|
|
77
|
+
updateArgs?: LatestUpdateArgsFn<TFn>;
|
|
78
|
+
}
|
|
79
|
+
/** See {@link LatestConstructor} */
|
|
80
|
+
export type LatestUpdateArgsFn<TFn extends AnyFn> = (
|
|
78
81
|
current: Parameters<TFn>,
|
|
79
82
|
middle: Parameters<TFn>[],
|
|
80
83
|
latest: Parameters<TFn>,
|
|
@@ -91,12 +94,12 @@ export class LatestImpl<TFn extends AnyFn> {
|
|
|
91
94
|
private middleArgs: Parameters<TFn>[];
|
|
92
95
|
|
|
93
96
|
private areArgsEqual: (a: Parameters<TFn>, b: Parameters<TFn>) => boolean;
|
|
94
|
-
private updateArgs:
|
|
97
|
+
private updateArgs: LatestUpdateArgsFn<TFn>;
|
|
95
98
|
|
|
96
99
|
constructor(
|
|
97
100
|
private fn: TFn,
|
|
98
101
|
areArgsEqual?: (a: Parameters<TFn>, b: Parameters<TFn>) => boolean,
|
|
99
|
-
updateArgs?:
|
|
102
|
+
updateArgs?: LatestUpdateArgsFn<TFn>,
|
|
100
103
|
) {
|
|
101
104
|
this.middleArgs = [];
|
|
102
105
|
this.areArgsEqual = areArgsEqual || (() => false);
|
package/src/sync/once.test.ts
CHANGED
package/src/sync/once.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { type AnyFn, type AwaitRet, makePromise } from "./util.ts";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Factory function for `once`. See {@link OnceConstructor} for usage
|
|
5
|
+
*/
|
|
6
|
+
export function once<TFn extends AnyFn>(args: OnceConstructor<TFn>) {
|
|
7
|
+
const impl = new OnceImpl(args.fn);
|
|
8
|
+
return (...args: Parameters<TFn>) => impl.invoke(...args);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Args for constructing a `once`
|
|
13
|
+
*
|
|
14
|
+
* `once` is an async event wrapper that ensures an async initialization is only ran once.
|
|
5
15
|
* Any subsequent calls after the first call will return a promise that resolves/rejects
|
|
6
16
|
* with the result of the first call.
|
|
7
17
|
*
|
|
@@ -66,15 +76,10 @@ import { type AnyFn, type AwaitRet, makePromise } from "./util.ts";
|
|
|
66
76
|
* This is not an issue if the resource doesn't leak other resources,
|
|
67
77
|
* since it will eventually be GC'd.
|
|
68
78
|
*/
|
|
69
|
-
export
|
|
70
|
-
const impl = new OnceImpl(fn);
|
|
71
|
-
return (...args: Parameters<TFn>) => impl.invoke(...args);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export type OnceConstructor<TFn> = {
|
|
79
|
+
export interface OnceConstructor<TFn> {
|
|
75
80
|
/** Function to be called only once */
|
|
76
81
|
fn: TFn;
|
|
77
|
-
}
|
|
82
|
+
}
|
|
78
83
|
|
|
79
84
|
export class OnceImpl<TFn extends AnyFn> {
|
|
80
85
|
private promise: Promise<AwaitRet<TFn>> | undefined;
|
package/src/sync/serial.test.ts
CHANGED
package/src/sync/serial.ts
CHANGED
|
@@ -2,7 +2,18 @@ import type { Result } from "../result/index.ts";
|
|
|
2
2
|
import type { AnyFn } from "./util.ts";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Factory function for `serial`. See {@link SerialConstructor} for usage
|
|
6
|
+
*/
|
|
7
|
+
export const serial = <TFn extends AnyFn>(args: SerialConstructor<TFn>) => {
|
|
8
|
+
const { fn, onCancel } = args;
|
|
9
|
+
const impl = new SerialImpl(fn, onCancel);
|
|
10
|
+
return (...args: Parameters<TFn>) => impl.invoke(...args);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Options for `serial` function
|
|
15
|
+
*
|
|
16
|
+
* `serial` is an async event wrapper that is cancelled when a new one starts.
|
|
6
17
|
* When a new event is started, the previous caller will receive a
|
|
7
18
|
* cancellation error, instead of being hung up indefinitely.
|
|
8
19
|
*
|
|
@@ -132,16 +143,7 @@ import type { AnyFn } from "./util.ts";
|
|
|
132
143
|
*
|
|
133
144
|
* If the underlying function throws, the exception will be re-thrown to the caller.
|
|
134
145
|
*/
|
|
135
|
-
|
|
136
|
-
export function serial<TFn extends AnyFn>({ fn, onCancel }: SerialConstructor<TFn>) {
|
|
137
|
-
const impl = new SerialImpl(fn, onCancel);
|
|
138
|
-
return (...args: Parameters<TFn>) => impl.invoke(...args);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Options for `serial` function
|
|
143
|
-
*/
|
|
144
|
-
export type SerialConstructor<TFn> = {
|
|
146
|
+
export interface SerialConstructor<TFn> {
|
|
145
147
|
/**
|
|
146
148
|
* Function creator that returns the async function to be wrapped
|
|
147
149
|
*/
|
|
@@ -152,7 +154,7 @@ export type SerialConstructor<TFn> = {
|
|
|
152
154
|
* This is guaranteed to be only called at most once per execution
|
|
153
155
|
*/
|
|
154
156
|
onCancel?: SerialEventCancelCallback;
|
|
155
|
-
}
|
|
157
|
+
}
|
|
156
158
|
|
|
157
159
|
class SerialImpl<TFn extends AnyFn> {
|
|
158
160
|
private serial: SerialId;
|
package/src/sync/util.ts
CHANGED
|
@@ -18,11 +18,11 @@ export const makePromise = <T>(): PromiseHandle<T> => {
|
|
|
18
18
|
* A handle of the promise that breaks down the promise object
|
|
19
19
|
* and its resolve and reject functions
|
|
20
20
|
*/
|
|
21
|
-
export
|
|
21
|
+
export interface PromiseHandle<T> {
|
|
22
22
|
promise: Promise<T>;
|
|
23
23
|
resolve: (value: T | PromiseLike<T>) => void;
|
|
24
24
|
reject: (reason?: unknown) => void;
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
26
|
|
|
27
27
|
/** Shorthand for Awaited<ReturnType<T>> */
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
package/src/fs/FsError.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { Result, Void } from "../result/index.ts";
|
|
2
|
-
|
|
3
|
-
/** Result type for file system operations */
|
|
4
|
-
export const FsErr = {
|
|
5
|
-
/** Generic error */
|
|
6
|
-
Fail: 1,
|
|
7
|
-
/** The operation does not apply to the root directory */
|
|
8
|
-
IsRoot: 2,
|
|
9
|
-
/** Invalid encoding */
|
|
10
|
-
InvalidEncoding: 3,
|
|
11
|
-
/** Not supported */
|
|
12
|
-
NotSupported: 4,
|
|
13
|
-
/** The operation does not apply to a file */
|
|
14
|
-
IsFile: 5,
|
|
15
|
-
/** The file was not modified since the last check */
|
|
16
|
-
NotModified: 6,
|
|
17
|
-
/** Permission error */
|
|
18
|
-
PermissionDenied: 7,
|
|
19
|
-
/** User abort */
|
|
20
|
-
UserAbort: 8,
|
|
21
|
-
/** Not found */
|
|
22
|
-
NotFound: 9,
|
|
23
|
-
/** Trying to do stuff to a closed file */
|
|
24
|
-
IsClosed: 10,
|
|
25
|
-
/** If the path is invalid, for example trying to get the parent of root */
|
|
26
|
-
InvalidPath: 11,
|
|
27
|
-
/** Trying to operate on a file that has been closed */
|
|
28
|
-
Closed: 12,
|
|
29
|
-
/** The operation does not apply to a directory */
|
|
30
|
-
IsDirectory: 5,
|
|
31
|
-
} as const;
|
|
32
|
-
|
|
33
|
-
/** Result type for file system operations */
|
|
34
|
-
export type FsErr = (typeof FsErr)[keyof typeof FsErr];
|
|
35
|
-
|
|
36
|
-
/** Fs error type with a code and message */
|
|
37
|
-
export type FsError = {
|
|
38
|
-
readonly code: FsErr;
|
|
39
|
-
readonly message: string;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/** Helper to create a FsError */
|
|
43
|
-
export function fsErr(code: FsErr, message: string): FsError {
|
|
44
|
-
return { code, message };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Helper to create a FsError with the code Fail */
|
|
48
|
-
export function fsFail(message: string): FsError {
|
|
49
|
-
return fsErr(FsErr.Fail, message);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** Helper result type for FsError */
|
|
53
|
-
export type FsResult<T> = Result<T, FsError>;
|
|
54
|
-
/** Helper result type for FsError with no value */
|
|
55
|
-
export type FsVoid = Void<FsError>;
|
package/src/fs/FsFile.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { FsResult, FsVoid } from "./FsError.ts";
|
|
2
|
-
|
|
3
|
-
/** Interface for operating on a file in the loaded file system */
|
|
4
|
-
export interface FsFile {
|
|
5
|
-
/** Path of the file relative to the root of the file system (the uploaded directory) */
|
|
6
|
-
readonly path: string;
|
|
7
|
-
|
|
8
|
-
/** Returns if the content of the file in memory is newer than the file on disk */
|
|
9
|
-
isDirty(): boolean;
|
|
10
|
-
|
|
11
|
-
/** Get the last modified time. May load it from file system if needed */
|
|
12
|
-
getLastModified(): Promise<FsResult<number>>;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Get the text content of the file
|
|
16
|
-
*
|
|
17
|
-
* If the file is not loaded, it will load it.
|
|
18
|
-
*
|
|
19
|
-
* If the file is not a text file, it will return InvalidEncoding
|
|
20
|
-
*/
|
|
21
|
-
getText(): Promise<FsResult<string>>;
|
|
22
|
-
|
|
23
|
-
/** Get the content of the file */
|
|
24
|
-
getBytes(): Promise<FsResult<Uint8Array>>;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Set the content in memory. Does not save to disk.
|
|
28
|
-
* Does nothing if file is closed
|
|
29
|
-
*/
|
|
30
|
-
setText(content: string): void;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Set the content in memory. Does not save to disk.
|
|
34
|
-
* Does nothing if file is closed
|
|
35
|
-
*/
|
|
36
|
-
setBytes(content: Uint8Array<ArrayBuffer>): void;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Load the file's content if it's not newer than fs
|
|
40
|
-
*
|
|
41
|
-
* Returns Ok if the file is newer than fs
|
|
42
|
-
*/
|
|
43
|
-
loadIfNotDirty(): Promise<FsVoid>;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Load the file's content from FS.
|
|
47
|
-
*
|
|
48
|
-
* Overwrites any unsaved changes in memory only if the file was modified
|
|
49
|
-
* at a later time than the last in memory modification.
|
|
50
|
-
*
|
|
51
|
-
* If it fails, the file's content in memory will not be changed
|
|
52
|
-
*/
|
|
53
|
-
load(): Promise<FsVoid>;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Save the file's content to FS if it is dirty.
|
|
57
|
-
*
|
|
58
|
-
* If not dirty, returns Ok
|
|
59
|
-
*/
|
|
60
|
-
writeIfNewer(): Promise<FsVoid>;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Close the file. In memory content will be lost.
|
|
64
|
-
* Further operations on the file will fail
|
|
65
|
-
*/
|
|
66
|
-
close(): void;
|
|
67
|
-
}
|
package/src/fs/FsFileImpl.ts
DELETED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
import { tryAsync, errstr } from "../result/index.ts";
|
|
2
|
-
|
|
3
|
-
import type { FsFile } from "./FsFile.ts";
|
|
4
|
-
import type { FsFileSystemInternal } from "./FsFileSystemInternal.ts";
|
|
5
|
-
import { FsErr, type FsResult, type FsVoid, fsErr, fsFail } from "./FsError.ts";
|
|
6
|
-
|
|
7
|
-
/** Allocate a new file object */
|
|
8
|
-
export function fsFile(fs: FsFileSystemInternal, path: string): FsFile {
|
|
9
|
-
return new FsFileImpl(fs, path);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function errclosed() {
|
|
13
|
-
return { err: fsErr(FsErr.Closed, "File is closed") } as const;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
class FsFileImpl implements FsFile {
|
|
17
|
-
/** The path of the file */
|
|
18
|
-
public path: string;
|
|
19
|
-
|
|
20
|
-
private closed: boolean;
|
|
21
|
-
|
|
22
|
-
/** Reference to the file system so we can read/write */
|
|
23
|
-
private fs: FsFileSystemInternal;
|
|
24
|
-
/** If the file is text */
|
|
25
|
-
private isText: boolean;
|
|
26
|
-
/** Bytes of the file */
|
|
27
|
-
private buffer: Uint8Array<ArrayBuffer> | undefined;
|
|
28
|
-
/** If the content in the buffer is different from the content on FS */
|
|
29
|
-
private isBufferDirty: boolean;
|
|
30
|
-
/** The content string of the file */
|
|
31
|
-
private content: string | undefined;
|
|
32
|
-
/** If the content string is newer than the bytes */
|
|
33
|
-
private isContentNewer: boolean;
|
|
34
|
-
/** The last modified time of the file */
|
|
35
|
-
private lastModified: number | undefined;
|
|
36
|
-
|
|
37
|
-
constructor(fs: FsFileSystemInternal, path: string) {
|
|
38
|
-
this.closed = false;
|
|
39
|
-
this.fs = fs;
|
|
40
|
-
this.path = path;
|
|
41
|
-
this.isText = false;
|
|
42
|
-
this.buffer = undefined;
|
|
43
|
-
this.isBufferDirty = false;
|
|
44
|
-
this.content = undefined;
|
|
45
|
-
this.isContentNewer = false;
|
|
46
|
-
this.lastModified = undefined;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
public close(): void {
|
|
50
|
-
this.closed = true;
|
|
51
|
-
this.fs.closeFile(this.path);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public isDirty(): boolean {
|
|
55
|
-
return this.isBufferDirty || this.isContentNewer;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
public async getLastModified(): Promise<FsResult<number>> {
|
|
59
|
-
if (this.closed) {
|
|
60
|
-
return errclosed();
|
|
61
|
-
}
|
|
62
|
-
if (this.lastModified === undefined) {
|
|
63
|
-
const r = await this.loadIfNotDirty();
|
|
64
|
-
if (r.err) {
|
|
65
|
-
return r;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return { val: this.lastModified ?? 0 };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
public async getText(): Promise<FsResult<string>> {
|
|
72
|
-
if (this.closed) {
|
|
73
|
-
return errclosed();
|
|
74
|
-
}
|
|
75
|
-
if (this.buffer === undefined) {
|
|
76
|
-
const r = await this.load();
|
|
77
|
-
if (r.err) {
|
|
78
|
-
return r;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (!this.isText) {
|
|
82
|
-
const err = fsFail("File is not valid UTF-8");
|
|
83
|
-
return { err };
|
|
84
|
-
}
|
|
85
|
-
return { val: this.content ?? "" };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
public async getBytes(): Promise<FsResult<Uint8Array>> {
|
|
89
|
-
if (this.closed) {
|
|
90
|
-
return errclosed();
|
|
91
|
-
}
|
|
92
|
-
this.updateBuffer();
|
|
93
|
-
if (this.buffer === undefined) {
|
|
94
|
-
const r = await this.load();
|
|
95
|
-
if (r.err) {
|
|
96
|
-
return r;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (this.buffer === undefined) {
|
|
100
|
-
const err = fsFail("Read was successful, but content was undefined");
|
|
101
|
-
return { err };
|
|
102
|
-
}
|
|
103
|
-
return { val: this.buffer };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
public setText(content: string): void {
|
|
107
|
-
if (this.closed) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (this.content === content) {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
this.content = content;
|
|
114
|
-
this.isContentNewer = true;
|
|
115
|
-
this.lastModified = new Date().getTime();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
public setBytes(content: Uint8Array<ArrayBuffer>): void {
|
|
119
|
-
if (this.closed) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
this.buffer = content;
|
|
123
|
-
this.isBufferDirty = true;
|
|
124
|
-
this.decodeBuffer();
|
|
125
|
-
this.isContentNewer = true;
|
|
126
|
-
this.lastModified = new Date().getTime();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
public async loadIfNotDirty(): Promise<FsVoid> {
|
|
130
|
-
if (this.closed) {
|
|
131
|
-
return errclosed();
|
|
132
|
-
}
|
|
133
|
-
if (this.isDirty()) {
|
|
134
|
-
return {};
|
|
135
|
-
}
|
|
136
|
-
return await this.load();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
public async load(): Promise<FsVoid> {
|
|
140
|
-
if (this.closed) {
|
|
141
|
-
return errclosed();
|
|
142
|
-
}
|
|
143
|
-
const { val: file, err } = await this.fs.read(this.path);
|
|
144
|
-
if (err) {
|
|
145
|
-
return { err };
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// check if the file has been modified since last loaded
|
|
149
|
-
if (this.lastModified !== undefined) {
|
|
150
|
-
if (file.lastModified <= this.lastModified) {
|
|
151
|
-
return {};
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
this.lastModified = file.lastModified;
|
|
155
|
-
// load the buffer
|
|
156
|
-
const buffer = await tryAsync(async () => new Uint8Array(await file.arrayBuffer()));
|
|
157
|
-
if ("err" in buffer) {
|
|
158
|
-
const err = fsFail(errstr(buffer.err));
|
|
159
|
-
return { err };
|
|
160
|
-
}
|
|
161
|
-
this.buffer = buffer.val;
|
|
162
|
-
this.isBufferDirty = false;
|
|
163
|
-
// Try decoding the buffer as text
|
|
164
|
-
this.decodeBuffer();
|
|
165
|
-
this.isContentNewer = false;
|
|
166
|
-
return {};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
public async writeIfNewer(): Promise<FsVoid> {
|
|
170
|
-
if (this.closed) {
|
|
171
|
-
return errclosed();
|
|
172
|
-
}
|
|
173
|
-
if (!this.isDirty()) {
|
|
174
|
-
return {};
|
|
175
|
-
}
|
|
176
|
-
return await this.write();
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Write the content without checking if it's dirty. Overwrites the file currently on FS
|
|
181
|
-
*
|
|
182
|
-
* This is private - outside code should only use writeIfDirty
|
|
183
|
-
*/
|
|
184
|
-
private async write(): Promise<FsVoid> {
|
|
185
|
-
this.updateBuffer();
|
|
186
|
-
const buffer = this.buffer;
|
|
187
|
-
if (this.content === undefined || buffer === undefined) {
|
|
188
|
-
// file was never read or modified
|
|
189
|
-
return {};
|
|
190
|
-
}
|
|
191
|
-
const result = await this.fs.write(this.path, buffer);
|
|
192
|
-
if (result.err) {
|
|
193
|
-
return result;
|
|
194
|
-
}
|
|
195
|
-
this.isBufferDirty = false;
|
|
196
|
-
return {};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
private decodeBuffer() {
|
|
200
|
-
try {
|
|
201
|
-
this.content = new TextDecoder("utf-8", { fatal: true }).decode(this.buffer);
|
|
202
|
-
this.isText = true;
|
|
203
|
-
} catch {
|
|
204
|
-
this.content = undefined;
|
|
205
|
-
this.isText = false;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/** Encode the content to buffer if it is newer */
|
|
210
|
-
private updateBuffer() {
|
|
211
|
-
if (!this.isContentNewer || this.content === undefined) {
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
const encoder = new TextEncoder();
|
|
215
|
-
this.buffer = encoder.encode(this.content);
|
|
216
|
-
this.isBufferDirty = true;
|
|
217
|
-
this.isContentNewer = false;
|
|
218
|
-
}
|
|
219
|
-
}
|
package/src/fs/FsFileMgr.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { FsFile } from "./FsFile.ts";
|
|
2
|
-
import type { FsFileSystemInternal } from "./FsFileSystemInternal.ts";
|
|
3
|
-
import { fsFile } from "./FsFileImpl.ts";
|
|
4
|
-
|
|
5
|
-
/** Internal class to track opened files */
|
|
6
|
-
export class FsFileMgr {
|
|
7
|
-
private opened: Map<string, FsFile>;
|
|
8
|
-
|
|
9
|
-
public constructor() {
|
|
10
|
-
this.opened = new Map();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
public get(fs: FsFileSystemInternal, path: string): FsFile {
|
|
14
|
-
let file = this.opened.get(path);
|
|
15
|
-
if (!file) {
|
|
16
|
-
file = fsFile(fs, path);
|
|
17
|
-
this.opened.set(path, file);
|
|
18
|
-
}
|
|
19
|
-
return file;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public close(path: string): void {
|
|
23
|
-
this.opened.delete(path);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public getOpenedPaths(): string[] {
|
|
27
|
-
return Array.from(this.opened.keys());
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { FsResult, FsVoid } from "./FsError.ts";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Interface for operating on a file opened standalone (without opening a file system
|
|
5
|
-
*/
|
|
6
|
-
export interface FsFileStandalone {
|
|
7
|
-
/** The name of the file */
|
|
8
|
-
readonly name: string;
|
|
9
|
-
/** If the file is writable. May prompt user for permission */
|
|
10
|
-
isWritable(): Promise<boolean>;
|
|
11
|
-
/** Get the size of the file */
|
|
12
|
-
getSize(): Promise<FsResult<number>>;
|
|
13
|
-
/** Get the content of the file as a byte array */
|
|
14
|
-
getBytes(): Promise<FsResult<Uint8Array>>;
|
|
15
|
-
/** Get the last modified time of the file */
|
|
16
|
-
getLastModified(): Promise<FsResult<number>>;
|
|
17
|
-
/** Get the text content of the file*/
|
|
18
|
-
getText(): Promise<FsResult<string>>;
|
|
19
|
-
/** Write content to the file if the implementation supports writing, and permission is granted*/
|
|
20
|
-
write(content: Uint8Array | string): Promise<FsVoid>;
|
|
21
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { ilog } from "../log/internal.ts";
|
|
2
|
-
import { errstr } from "../result";
|
|
3
|
-
|
|
4
|
-
import { fsErr, FsErr, fsFail, type FsVoid, type FsResult } from "./FsError.ts";
|
|
5
|
-
import type { FsFileStandalone } from "./FsFileStandalone.ts";
|
|
6
|
-
|
|
7
|
-
export class FsFileStandaloneImplFileAPI implements FsFileStandalone {
|
|
8
|
-
public name: string;
|
|
9
|
-
private size: number;
|
|
10
|
-
private lastModified: number;
|
|
11
|
-
private file: File;
|
|
12
|
-
|
|
13
|
-
constructor(file: File) {
|
|
14
|
-
this.name = file.name;
|
|
15
|
-
this.size = file.size;
|
|
16
|
-
this.lastModified = file.lastModified;
|
|
17
|
-
this.file = file;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
public async isWritable(): Promise<boolean> {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
public async getSize(): Promise<FsResult<number>> {
|
|
25
|
-
return { val: this.size };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public async getBytes(): Promise<FsResult<Uint8Array>> {
|
|
29
|
-
try {
|
|
30
|
-
const data = await this.file.arrayBuffer();
|
|
31
|
-
return { val: new Uint8Array(data) };
|
|
32
|
-
} catch (e) {
|
|
33
|
-
ilog.error(e);
|
|
34
|
-
return { err: fsFail(errstr(e)) };
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
public async getLastModified(): Promise<FsResult<number>> {
|
|
38
|
-
return { val: this.lastModified };
|
|
39
|
-
}
|
|
40
|
-
public async getText(): Promise<FsResult<string>> {
|
|
41
|
-
try {
|
|
42
|
-
const data = await this.file.text();
|
|
43
|
-
return { val: data };
|
|
44
|
-
} catch (e) {
|
|
45
|
-
ilog.error(e);
|
|
46
|
-
return { err: fsFail(errstr(e)) };
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
public async write(): Promise<FsVoid> {
|
|
50
|
-
return {
|
|
51
|
-
err: fsErr(FsErr.NotSupported, "Write not supported in File API"),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|