@zenfs/core 1.9.5 → 1.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.
@@ -16,10 +16,10 @@ export interface FetchOptions extends SharedConfig {
16
16
  * Defaults to `index.json`.
17
17
  */
18
18
  index?: string | IndexData;
19
- /** Used as the URL prefix for fetched files.
20
- * Default: Fetch files relative to the index.
19
+ /**
20
+ * Used as the URL prefix for fetched files.
21
21
  */
22
- baseUrl?: string;
22
+ baseUrl: string;
23
23
  /**
24
24
  * If true, enables writing to the remote (using post and delete)
25
25
  * @default false
@@ -56,7 +56,7 @@ declare const _Fetch: {
56
56
  };
57
57
  readonly baseUrl: {
58
58
  readonly type: "string";
59
- readonly required: false;
59
+ readonly required: true;
60
60
  };
61
61
  readonly requestInit: {
62
62
  readonly type: "object";
@@ -88,7 +88,7 @@ const _Fetch = {
88
88
  name: 'Fetch',
89
89
  options: {
90
90
  index: { type: ['string', 'object'], required: false },
91
- baseUrl: { type: 'string', required: false },
91
+ baseUrl: { type: 'string', required: true },
92
92
  requestInit: { type: 'object', required: false },
93
93
  remoteWrite: { type: 'boolean', required: false },
94
94
  },
@@ -97,7 +97,7 @@ const _Fetch = {
97
97
  },
98
98
  async create(options) {
99
99
  var _a;
100
- const url = new URL(options.baseUrl || '');
100
+ const url = new URL(options.baseUrl);
101
101
  url.pathname = normalizePath(url.pathname);
102
102
  let baseUrl = url.toString();
103
103
  if (baseUrl.at(-1) == '/')
@@ -4,6 +4,7 @@ export * from './memory.js';
4
4
  export * from './overlay.js';
5
5
  export * from './passthrough.js';
6
6
  export * from './port/fs.js';
7
+ export * from './single_buffer.js';
7
8
  export * from './store/fs.js';
8
9
  export * from './store/map.js';
9
10
  export * from './store/store.js';
@@ -4,6 +4,7 @@ export * from './memory.js';
4
4
  export * from './overlay.js';
5
5
  export * from './passthrough.js';
6
6
  export * from './port/fs.js';
7
+ export * from './single_buffer.js';
7
8
  export * from './store/fs.js';
8
9
  export * from './store/map.js';
9
10
  export * from './store/store.js';
@@ -1,3 +1,4 @@
1
+ import type { UsageInfo } from '../internal/filesystem.js';
1
2
  import { StoreFS } from './store/fs.js';
2
3
  import { SyncMapTransaction, type SyncMapStore } from './store/map.js';
3
4
  /**
@@ -5,24 +6,45 @@ import { SyncMapTransaction, type SyncMapStore } from './store/map.js';
5
6
  * @category Stores and Transactions
6
7
  */
7
8
  export declare class InMemoryStore extends Map<number, Uint8Array> implements SyncMapStore {
9
+ readonly maxSize: number;
8
10
  readonly label?: string | undefined;
9
11
  readonly flags: readonly [];
10
12
  readonly name = "tmpfs";
11
- constructor(label?: string | undefined);
13
+ constructor(maxSize?: number, label?: string | undefined);
12
14
  sync(): Promise<void>;
13
15
  transaction(): SyncMapTransaction;
16
+ get bytes(): number;
17
+ usage(): UsageInfo;
18
+ }
19
+ /**
20
+ * Options for an in-memory backend
21
+ * @category Backends and Configuration
22
+ */
23
+ export interface InMemoryOptions {
24
+ /** The maximum size of the store. Defaults to 4 GiB */
25
+ maxSize?: number;
26
+ /** The label to use for the store and file system */
27
+ label?: string;
28
+ /** @deprecated use `label` */
29
+ name?: string;
14
30
  }
15
31
  declare const _InMemory: {
16
32
  readonly name: "InMemory";
17
33
  readonly options: {
34
+ readonly maxSize: {
35
+ readonly type: "number";
36
+ readonly required: false;
37
+ };
38
+ readonly label: {
39
+ readonly type: "string";
40
+ readonly required: false;
41
+ };
18
42
  readonly name: {
19
43
  readonly type: "string";
20
44
  readonly required: false;
21
45
  };
22
46
  };
23
- readonly create: ({ name }: {
24
- name?: string;
25
- }) => StoreFS<InMemoryStore>;
47
+ readonly create: ({ maxSize, label, name }: InMemoryOptions) => StoreFS<InMemoryStore>;
26
48
  };
27
49
  type _InMemory = typeof _InMemory;
28
50
  export interface InMemory extends _InMemory {
@@ -1,3 +1,4 @@
1
+ import { size_max } from '../vfs/constants.js';
1
2
  import { StoreFS } from './store/fs.js';
2
3
  import { SyncMapTransaction } from './store/map.js';
3
4
  /**
@@ -5,8 +6,9 @@ import { SyncMapTransaction } from './store/map.js';
5
6
  * @category Stores and Transactions
6
7
  */
7
8
  export class InMemoryStore extends Map {
8
- constructor(label) {
9
+ constructor(maxSize = size_max, label) {
9
10
  super();
11
+ this.maxSize = maxSize;
10
12
  this.label = label;
11
13
  this.flags = [];
12
14
  this.name = 'tmpfs';
@@ -15,14 +17,28 @@ export class InMemoryStore extends Map {
15
17
  transaction() {
16
18
  return new SyncMapTransaction(this);
17
19
  }
20
+ get bytes() {
21
+ let size = this.size * 4;
22
+ for (const data of this.values())
23
+ size += data.byteLength;
24
+ return size;
25
+ }
26
+ usage() {
27
+ return {
28
+ totalSpace: this.maxSize,
29
+ freeSpace: this.maxSize - this.bytes,
30
+ };
31
+ }
18
32
  }
19
33
  const _InMemory = {
20
34
  name: 'InMemory',
21
35
  options: {
36
+ maxSize: { type: 'number', required: false },
37
+ label: { type: 'string', required: false },
22
38
  name: { type: 'string', required: false },
23
39
  },
24
- create({ name }) {
25
- const fs = new StoreFS(new InMemoryStore(name));
40
+ create({ maxSize, label, name }) {
41
+ const fs = new StoreFS(new InMemoryStore(maxSize, label !== null && label !== void 0 ? label : name));
26
42
  fs.checkRootSync();
27
43
  return fs;
28
44
  },
@@ -1,5 +1,5 @@
1
1
  import type { File } from '../internal/file.js';
2
- import type { CreationOptions } from '../internal/filesystem.js';
2
+ import type { CreationOptions, UsageInfo } from '../internal/filesystem.js';
3
3
  import type { Stats } from '../stats.js';
4
4
  import type { InodeLike } from '../internal/inode.js';
5
5
  import { FileSystem } from '../internal/filesystem.js';
@@ -37,6 +37,10 @@ export declare class OverlayFS extends FileSystem {
37
37
  private _deleteLogError?;
38
38
  private _ready;
39
39
  constructor({ writable, readable }: OverlayOptions);
40
+ /**
41
+ * @todo Consider trying to track information on the writable as well
42
+ */
43
+ usage(): UsageInfo;
40
44
  sync(path: string, data: Uint8Array, stats: Readonly<InodeLike>): Promise<void>;
41
45
  syncSync(path: string, data: Uint8Array, stats: Readonly<InodeLike>): void;
42
46
  read(path: string, buffer: Uint8Array, offset: number, end: number): Promise<void>;
@@ -86,10 +86,16 @@ export class OverlayFS extends FileSystem {
86
86
  this.writable = writable;
87
87
  this.readable = readable;
88
88
  if (this.writable.attributes.has('no_write')) {
89
- throw err(new ErrnoError(Errno.EINVAL, 'Writable file can not be written to'));
89
+ throw err(new ErrnoError(Errno.EINVAL, 'Writable file system can not be written to'));
90
90
  }
91
91
  this._ready = this._initialize();
92
92
  }
93
+ /**
94
+ * @todo Consider trying to track information on the writable as well
95
+ */
96
+ usage() {
97
+ return this.readable.usage();
98
+ }
93
99
  async sync(path, data, stats) {
94
100
  await this.copyForWrite(path);
95
101
  await this.writable.sync(path, data, stats);
@@ -1,6 +1,6 @@
1
1
  import type * as fs from 'node:fs';
2
2
  import { File } from '../internal/file.js';
3
- import { FileSystem } from '../internal/filesystem.js';
3
+ import { FileSystem, type UsageInfo } from '../internal/filesystem.js';
4
4
  import type { InodeLike } from '../internal/inode.js';
5
5
  import { Stats } from '../stats.js';
6
6
  export type NodeFS = typeof fs;
@@ -16,6 +16,7 @@ export declare class PassthroughFS extends FileSystem {
16
16
  readonly nodeFS: NodeFS;
17
17
  readonly prefix: string;
18
18
  constructor(nodeFS: NodeFS, prefix: string);
19
+ usage(): UsageInfo;
19
20
  path(path: string): string;
20
21
  error(err: unknown, path: string): never;
21
22
  /**
@@ -141,6 +141,13 @@ export class PassthroughFS extends FileSystem {
141
141
  this.nodeFS = nodeFS;
142
142
  this.prefix = prefix;
143
143
  }
144
+ usage() {
145
+ const info = this.nodeFS.statfsSync(this.prefix);
146
+ return {
147
+ totalSpace: info.bsize * info.blocks,
148
+ freeSpace: info.bsize * info.bfree,
149
+ };
150
+ }
144
151
  path(path) {
145
152
  return join(this.prefix, path.slice(1));
146
153
  }
@@ -0,0 +1,149 @@
1
+ import type { UsageInfo } from '../internal/filesystem.js';
2
+ import { StoreFS } from './store/fs.js';
3
+ import { SyncMapTransaction, type SyncMapStore } from './store/map.js';
4
+ import type { Store } from './store/store.js';
5
+ declare class MetadataEntry {
6
+ /** Inode or data ID */
7
+ id: number;
8
+ /** Reserved for 64-bit offset expansion */
9
+ protected offset_: number;
10
+ /** Offset into the buffer the data is stored at. */
11
+ offset: number;
12
+ /** The size of the data */
13
+ size: number;
14
+ }
15
+ /**
16
+ * A block of metadata for a single-buffer file system.
17
+ * This metadata maps IDs (for inodes and data) to actual offsets in the buffer.
18
+ * This is done since IDs are not guaranteed to be sequential.
19
+ */
20
+ declare class MetadataBlock {
21
+ protected readonly superblock: SuperBlock;
22
+ offset: number;
23
+ constructor(superblock: SuperBlock, offset?: number);
24
+ /**
25
+ * The crc32c checksum for the metadata block.
26
+ * @privateRemarks Keep this first!
27
+ */
28
+ checksum: number;
29
+ /** The (last) time this metadata block was updated */
30
+ timestamp: number;
31
+ /** Reserved for 64-bit offset expansion */
32
+ protected previous_offset_: number;
33
+ /** Offset to the previous metadata block */
34
+ previous_offset: number;
35
+ protected _previous?: MetadataBlock;
36
+ get previous(): MetadataBlock | undefined;
37
+ /** Metadata entries. */
38
+ entries: MetadataEntry[];
39
+ }
40
+ /**
41
+ * The super block structure for a single-buffer file system
42
+ */
43
+ declare class SuperBlock {
44
+ readonly store: SingleBufferStore;
45
+ constructor(store: SingleBufferStore);
46
+ /**
47
+ * The crc32c checksum for the super block.
48
+ * @privateRemarks Keep this first!
49
+ */
50
+ checksum: number;
51
+ /** Signature for the superblock. */
52
+ magic: number;
53
+ /** The version of the on-disk format */
54
+ version: number;
55
+ /** Which format of `Inode` is used */
56
+ inode_format: number;
57
+ /** Flags for the file system. Currently unused */
58
+ flags: number;
59
+ /** The number of used bytes, including the super block and metadata */
60
+ used_bytes: bigint;
61
+ /** The total size of the entire file system, including the super block and metadata */
62
+ total_bytes: bigint;
63
+ /** An ID for this file system */
64
+ id: bigint;
65
+ /**
66
+ * The size in bytes of a metadata block.
67
+ * Not currently configurable.
68
+ */
69
+ metadata_block_size: number;
70
+ /** Reserved for 64-bit offset expansion */
71
+ protected metadata_offset_: number;
72
+ /** Offset of the current metadata block */
73
+ metadata_offset: number;
74
+ metadata: MetadataBlock;
75
+ /** An optional label for the file system */
76
+ label: string;
77
+ /** Padded to 256 bytes */
78
+ _padding: number[];
79
+ /**
80
+ * Rotate out the current metadata block.
81
+ * Allocates a new metadata block, moves the current one to backup,
82
+ * and updates used_bytes accordingly.
83
+ * @returns the new metadata block
84
+ */
85
+ rotateMetadata(): MetadataBlock;
86
+ /**
87
+ * Checks to see if `length` bytes are unused, starting at `offset`.
88
+ * @internal Not for external use!
89
+ */
90
+ isUnused(offset: number, length: number): boolean;
91
+ }
92
+ /**
93
+ *
94
+ * @category Stores and Transactions
95
+ */
96
+ export declare class SingleBufferStore implements SyncMapStore {
97
+ readonly flags: readonly [];
98
+ readonly name = "sbfs";
99
+ readonly id = 1935828595;
100
+ protected superblock: SuperBlock;
101
+ /**
102
+ * @internal @hidden
103
+ */
104
+ readonly _view: DataView;
105
+ /**
106
+ * @internal @hidden
107
+ */
108
+ readonly _buffer: Uint8Array;
109
+ constructor(buffer: ArrayBufferLike | ArrayBufferView);
110
+ /**
111
+ * Update a block's checksum and write it to the store's buffer.
112
+ * @internal @hidden
113
+ */
114
+ _write(value: SuperBlock | MetadataBlock): void;
115
+ keys(): Iterable<number>;
116
+ get(id: number): Uint8Array | undefined;
117
+ set(id: number, data: Uint8Array): void;
118
+ delete(id: number): void;
119
+ _fs?: StoreFS<Store> | undefined;
120
+ sync(): Promise<void>;
121
+ usage(): UsageInfo;
122
+ transaction(): SyncMapTransaction;
123
+ }
124
+ /**
125
+ * Options for the `SingleBuffer` backend
126
+ * @category Backends and Configuration
127
+ */
128
+ export interface SingleBufferOptions {
129
+ buffer: ArrayBufferLike | ArrayBufferView;
130
+ }
131
+ declare const _SingleBuffer: {
132
+ readonly name: "SingleBuffer";
133
+ readonly options: {
134
+ readonly buffer: {
135
+ readonly type: "object";
136
+ readonly required: true;
137
+ };
138
+ };
139
+ readonly create: ({ buffer }: SingleBufferOptions) => StoreFS<SingleBufferStore>;
140
+ };
141
+ type _SingleBuffer = typeof _SingleBuffer;
142
+ export interface SingleBuffer extends _SingleBuffer {
143
+ }
144
+ /**
145
+ * A backend that uses a single buffer for storing data
146
+ * @category Backends and Configuration
147
+ */
148
+ export declare const SingleBuffer: SingleBuffer;
149
+ export {};
@@ -0,0 +1,498 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
36
+ if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
37
+ return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
38
+ };
39
+ import { deserialize, member, offsetof, serialize, sizeof, struct, types as t } from 'utilium';
40
+ import { crc32c } from 'utilium/checksum.js';
41
+ import { Errno, ErrnoError } from '../internal/error.js';
42
+ import { _inode_version } from '../internal/inode.js';
43
+ import { crit, warn } from '../internal/log.js';
44
+ import { StoreFS } from './store/fs.js';
45
+ import { SyncMapTransaction } from './store/map.js';
46
+ let MetadataEntry = (() => {
47
+ var _a, _b, _c, _d;
48
+ let _classDecorators = [struct()];
49
+ let _classDescriptor;
50
+ let _classExtraInitializers = [];
51
+ let _classThis;
52
+ let _id_decorators;
53
+ let _id_initializers = [];
54
+ let _id_extraInitializers = [];
55
+ let _offset__decorators;
56
+ let _offset__initializers = [];
57
+ let _offset__extraInitializers = [];
58
+ let _offset_decorators;
59
+ let _offset_initializers = [];
60
+ let _offset_extraInitializers = [];
61
+ let _size_decorators;
62
+ let _size_initializers = [];
63
+ let _size_extraInitializers = [];
64
+ var MetadataEntry = _classThis = class {
65
+ constructor() {
66
+ /** Inode or data ID */
67
+ this.id = __runInitializers(this, _id_initializers, 0);
68
+ /** Reserved for 64-bit offset expansion */
69
+ this.offset_ = (__runInitializers(this, _id_extraInitializers), __runInitializers(this, _offset__initializers, 0));
70
+ /** Offset into the buffer the data is stored at. */
71
+ this.offset = (__runInitializers(this, _offset__extraInitializers), __runInitializers(this, _offset_initializers, 0));
72
+ /** The size of the data */
73
+ this.size = (__runInitializers(this, _offset_extraInitializers), __runInitializers(this, _size_initializers, 0));
74
+ __runInitializers(this, _size_extraInitializers);
75
+ }
76
+ };
77
+ __setFunctionName(_classThis, "MetadataEntry");
78
+ (() => {
79
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
80
+ _id_decorators = [(_a = t).uint32.bind(_a)];
81
+ _offset__decorators = [(_b = t).uint32.bind(_b)];
82
+ _offset_decorators = [(_c = t).uint32.bind(_c)];
83
+ _size_decorators = [(_d = t).uint32.bind(_d)];
84
+ __esDecorate(null, null, _id_decorators, { kind: "field", name: "id", static: false, private: false, access: { has: obj => "id" in obj, get: obj => obj.id, set: (obj, value) => { obj.id = value; } }, metadata: _metadata }, _id_initializers, _id_extraInitializers);
85
+ __esDecorate(null, null, _offset__decorators, { kind: "field", name: "offset_", static: false, private: false, access: { has: obj => "offset_" in obj, get: obj => obj.offset_, set: (obj, value) => { obj.offset_ = value; } }, metadata: _metadata }, _offset__initializers, _offset__extraInitializers);
86
+ __esDecorate(null, null, _offset_decorators, { kind: "field", name: "offset", static: false, private: false, access: { has: obj => "offset" in obj, get: obj => obj.offset, set: (obj, value) => { obj.offset = value; } }, metadata: _metadata }, _offset_initializers, _offset_extraInitializers);
87
+ __esDecorate(null, null, _size_decorators, { kind: "field", name: "size", static: false, private: false, access: { has: obj => "size" in obj, get: obj => obj.size, set: (obj, value) => { obj.size = value; } }, metadata: _metadata }, _size_initializers, _size_extraInitializers);
88
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
89
+ MetadataEntry = _classThis = _classDescriptor.value;
90
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
91
+ __runInitializers(_classThis, _classExtraInitializers);
92
+ })();
93
+ return MetadataEntry = _classThis;
94
+ })();
95
+ /**
96
+ * Number of entries per block of metadata
97
+ */
98
+ const entries_per_block = 255;
99
+ /**
100
+ * A block of metadata for a single-buffer file system.
101
+ * This metadata maps IDs (for inodes and data) to actual offsets in the buffer.
102
+ * This is done since IDs are not guaranteed to be sequential.
103
+ */
104
+ let MetadataBlock = (() => {
105
+ var _a, _b, _c, _d;
106
+ let _classDecorators = [struct()];
107
+ let _classDescriptor;
108
+ let _classExtraInitializers = [];
109
+ let _classThis;
110
+ let _checksum_decorators;
111
+ let _checksum_initializers = [];
112
+ let _checksum_extraInitializers = [];
113
+ let _timestamp_decorators;
114
+ let _timestamp_initializers = [];
115
+ let _timestamp_extraInitializers = [];
116
+ let _previous_offset__decorators;
117
+ let _previous_offset__initializers = [];
118
+ let _previous_offset__extraInitializers = [];
119
+ let _previous_offset_decorators;
120
+ let _previous_offset_initializers = [];
121
+ let _previous_offset_extraInitializers = [];
122
+ let _entries_decorators;
123
+ let _entries_initializers = [];
124
+ let _entries_extraInitializers = [];
125
+ var MetadataBlock = _classThis = class {
126
+ constructor(superblock, offset = 0) {
127
+ this.superblock = superblock;
128
+ this.offset = offset;
129
+ /**
130
+ * The crc32c checksum for the metadata block.
131
+ * @privateRemarks Keep this first!
132
+ */
133
+ this.checksum = __runInitializers(this, _checksum_initializers, 0);
134
+ /** The (last) time this metadata block was updated */
135
+ this.timestamp = (__runInitializers(this, _checksum_extraInitializers), __runInitializers(this, _timestamp_initializers, Date.now()));
136
+ /** Reserved for 64-bit offset expansion */
137
+ this.previous_offset_ = (__runInitializers(this, _timestamp_extraInitializers), __runInitializers(this, _previous_offset__initializers, 0));
138
+ /** Offset to the previous metadata block */
139
+ this.previous_offset = (__runInitializers(this, _previous_offset__extraInitializers), __runInitializers(this, _previous_offset_initializers, 0));
140
+ this._previous = __runInitializers(this, _previous_offset_extraInitializers);
141
+ /** Metadata entries. */
142
+ this.entries = __runInitializers(this, _entries_initializers, Array.from({ length: entries_per_block }, () => new MetadataEntry()));
143
+ __runInitializers(this, _entries_extraInitializers);
144
+ this.superblock = superblock;
145
+ this.offset = offset;
146
+ if (!offset)
147
+ return; // fresh block
148
+ deserialize(this, superblock.store._buffer.subarray(offset, offset + sizeof(MetadataBlock)));
149
+ if (!checksumMatches(this))
150
+ throw crit(new ErrnoError(Errno.EIO, 'SingleBuffer: Checksum mismatch for metadata block at 0x' + offset.toString(16)));
151
+ }
152
+ get previous() {
153
+ var _a;
154
+ if (!this.previous_offset)
155
+ return;
156
+ (_a = this._previous) !== null && _a !== void 0 ? _a : (this._previous = new MetadataBlock(this.superblock, this.previous_offset));
157
+ return this._previous;
158
+ }
159
+ };
160
+ __setFunctionName(_classThis, "MetadataBlock");
161
+ (() => {
162
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
163
+ _checksum_decorators = [(_a = t).uint32.bind(_a)];
164
+ _timestamp_decorators = [(_b = t).uint32.bind(_b)];
165
+ _previous_offset__decorators = [(_c = t).uint32.bind(_c)];
166
+ _previous_offset_decorators = [(_d = t).uint32.bind(_d)];
167
+ _entries_decorators = [member(MetadataEntry, entries_per_block)];
168
+ __esDecorate(null, null, _checksum_decorators, { kind: "field", name: "checksum", static: false, private: false, access: { has: obj => "checksum" in obj, get: obj => obj.checksum, set: (obj, value) => { obj.checksum = value; } }, metadata: _metadata }, _checksum_initializers, _checksum_extraInitializers);
169
+ __esDecorate(null, null, _timestamp_decorators, { kind: "field", name: "timestamp", static: false, private: false, access: { has: obj => "timestamp" in obj, get: obj => obj.timestamp, set: (obj, value) => { obj.timestamp = value; } }, metadata: _metadata }, _timestamp_initializers, _timestamp_extraInitializers);
170
+ __esDecorate(null, null, _previous_offset__decorators, { kind: "field", name: "previous_offset_", static: false, private: false, access: { has: obj => "previous_offset_" in obj, get: obj => obj.previous_offset_, set: (obj, value) => { obj.previous_offset_ = value; } }, metadata: _metadata }, _previous_offset__initializers, _previous_offset__extraInitializers);
171
+ __esDecorate(null, null, _previous_offset_decorators, { kind: "field", name: "previous_offset", static: false, private: false, access: { has: obj => "previous_offset" in obj, get: obj => obj.previous_offset, set: (obj, value) => { obj.previous_offset = value; } }, metadata: _metadata }, _previous_offset_initializers, _previous_offset_extraInitializers);
172
+ __esDecorate(null, null, _entries_decorators, { kind: "field", name: "entries", static: false, private: false, access: { has: obj => "entries" in obj, get: obj => obj.entries, set: (obj, value) => { obj.entries = value; } }, metadata: _metadata }, _entries_initializers, _entries_extraInitializers);
173
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
174
+ MetadataBlock = _classThis = _classDescriptor.value;
175
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
176
+ __runInitializers(_classThis, _classExtraInitializers);
177
+ })();
178
+ return MetadataBlock = _classThis;
179
+ })();
180
+ const sb_magic = 0x7a2e7362; // 'z.sb'
181
+ /**
182
+ * The super block structure for a single-buffer file system
183
+ */
184
+ let SuperBlock = (() => {
185
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
186
+ let _classDecorators = [struct()];
187
+ let _classDescriptor;
188
+ let _classExtraInitializers = [];
189
+ let _classThis;
190
+ let _checksum_decorators;
191
+ let _checksum_initializers = [];
192
+ let _checksum_extraInitializers = [];
193
+ let _magic_decorators;
194
+ let _magic_initializers = [];
195
+ let _magic_extraInitializers = [];
196
+ let _version_decorators;
197
+ let _version_initializers = [];
198
+ let _version_extraInitializers = [];
199
+ let _inode_format_decorators;
200
+ let _inode_format_initializers = [];
201
+ let _inode_format_extraInitializers = [];
202
+ let _flags_decorators;
203
+ let _flags_initializers = [];
204
+ let _flags_extraInitializers = [];
205
+ let _used_bytes_decorators;
206
+ let _used_bytes_initializers = [];
207
+ let _used_bytes_extraInitializers = [];
208
+ let _total_bytes_decorators;
209
+ let _total_bytes_initializers = [];
210
+ let _total_bytes_extraInitializers = [];
211
+ let _id_decorators;
212
+ let _id_initializers = [];
213
+ let _id_extraInitializers = [];
214
+ let _metadata_block_size_decorators;
215
+ let _metadata_block_size_initializers = [];
216
+ let _metadata_block_size_extraInitializers = [];
217
+ let _metadata_offset__decorators;
218
+ let _metadata_offset__initializers = [];
219
+ let _metadata_offset__extraInitializers = [];
220
+ let _metadata_offset_decorators;
221
+ let _metadata_offset_initializers = [];
222
+ let _metadata_offset_extraInitializers = [];
223
+ let _label_decorators;
224
+ let _label_initializers = [];
225
+ let _label_extraInitializers = [];
226
+ let __padding_decorators;
227
+ let __padding_initializers = [];
228
+ let __padding_extraInitializers = [];
229
+ var SuperBlock = _classThis = class {
230
+ constructor(store) {
231
+ this.store = store;
232
+ /**
233
+ * The crc32c checksum for the super block.
234
+ * @privateRemarks Keep this first!
235
+ */
236
+ this.checksum = __runInitializers(this, _checksum_initializers, 0);
237
+ /** Signature for the superblock. */
238
+ this.magic = (__runInitializers(this, _checksum_extraInitializers), __runInitializers(this, _magic_initializers, sb_magic));
239
+ /** The version of the on-disk format */
240
+ this.version = (__runInitializers(this, _magic_extraInitializers), __runInitializers(this, _version_initializers, 1));
241
+ /** Which format of `Inode` is used */
242
+ this.inode_format = (__runInitializers(this, _version_extraInitializers), __runInitializers(this, _inode_format_initializers, _inode_version));
243
+ /** Flags for the file system. Currently unused */
244
+ this.flags = (__runInitializers(this, _inode_format_extraInitializers), __runInitializers(this, _flags_initializers, 0));
245
+ /** The number of used bytes, including the super block and metadata */
246
+ this.used_bytes = (__runInitializers(this, _flags_extraInitializers), __runInitializers(this, _used_bytes_initializers, BigInt(0)));
247
+ /** The total size of the entire file system, including the super block and metadata */
248
+ this.total_bytes = (__runInitializers(this, _used_bytes_extraInitializers), __runInitializers(this, _total_bytes_initializers, BigInt(0)));
249
+ /** An ID for this file system */
250
+ this.id = (__runInitializers(this, _total_bytes_extraInitializers), __runInitializers(this, _id_initializers, BigInt(0)));
251
+ /**
252
+ * The size in bytes of a metadata block.
253
+ * Not currently configurable.
254
+ */
255
+ this.metadata_block_size = (__runInitializers(this, _id_extraInitializers), __runInitializers(this, _metadata_block_size_initializers, sizeof(MetadataBlock)));
256
+ /** Reserved for 64-bit offset expansion */
257
+ this.metadata_offset_ = (__runInitializers(this, _metadata_block_size_extraInitializers), __runInitializers(this, _metadata_offset__initializers, 0));
258
+ /** Offset of the current metadata block */
259
+ this.metadata_offset = (__runInitializers(this, _metadata_offset__extraInitializers), __runInitializers(this, _metadata_offset_initializers, 0));
260
+ this.metadata = __runInitializers(this, _metadata_offset_extraInitializers);
261
+ /** An optional label for the file system */
262
+ this.label = __runInitializers(this, _label_initializers, '');
263
+ /** Padded to 256 bytes */
264
+ this._padding = (__runInitializers(this, _label_extraInitializers), __runInitializers(this, __padding_initializers, new Array(132).fill(0)));
265
+ __runInitializers(this, __padding_extraInitializers);
266
+ this.store = store;
267
+ if (store._view.getUint32(offsetof(SuperBlock, 'magic'), true) != sb_magic) {
268
+ warn('SingleBuffer: Invalid magic value. Assuming this is a fresh super block.');
269
+ this.metadata = new MetadataBlock(this);
270
+ this.used_bytes = BigInt(sizeof(SuperBlock) + sizeof(MetadataBlock));
271
+ this.total_bytes = BigInt(store._buffer.byteLength);
272
+ store._write(this);
273
+ store._write(this.metadata);
274
+ return;
275
+ }
276
+ deserialize(this, store._buffer.subarray(0, sizeof(SuperBlock)));
277
+ if (!checksumMatches(this))
278
+ throw crit(new ErrnoError(Errno.EIO, 'SingleBuffer: Checksum mismatch for super block!'));
279
+ this.metadata = new MetadataBlock(this, this.metadata_offset);
280
+ }
281
+ /**
282
+ * Rotate out the current metadata block.
283
+ * Allocates a new metadata block, moves the current one to backup,
284
+ * and updates used_bytes accordingly.
285
+ * @returns the new metadata block
286
+ */
287
+ rotateMetadata() {
288
+ const metadata = new MetadataBlock(this);
289
+ metadata.offset = Number(this.used_bytes);
290
+ metadata.previous_offset = this.metadata_offset;
291
+ this.metadata = metadata;
292
+ this.metadata_offset = metadata.offset;
293
+ this.store._write(metadata);
294
+ this.used_bytes += BigInt(sizeof(MetadataBlock));
295
+ this.store._write(this);
296
+ return metadata;
297
+ }
298
+ /**
299
+ * Checks to see if `length` bytes are unused, starting at `offset`.
300
+ * @internal Not for external use!
301
+ */
302
+ isUnused(offset, length) {
303
+ if (!length)
304
+ return true;
305
+ if (offset + length > this.total_bytes || offset < sizeof(SuperBlock))
306
+ return false;
307
+ for (let block = this.metadata; block; block = block.previous) {
308
+ if (offset < block.offset + sizeof(MetadataBlock) && offset + length > block.offset)
309
+ return false;
310
+ for (const entry of block.entries) {
311
+ if (!entry.offset)
312
+ continue;
313
+ if ((offset >= entry.offset && offset < entry.offset + entry.size)
314
+ || (offset + length > entry.offset && offset + length <= entry.offset + entry.size)
315
+ || (offset <= entry.offset && offset + length >= entry.offset + entry.size)) {
316
+ return false;
317
+ }
318
+ }
319
+ }
320
+ return true;
321
+ }
322
+ };
323
+ __setFunctionName(_classThis, "SuperBlock");
324
+ (() => {
325
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
326
+ _checksum_decorators = [(_a = t).uint32.bind(_a)];
327
+ _magic_decorators = [(_b = t).uint32.bind(_b)];
328
+ _version_decorators = [(_c = t).uint16.bind(_c)];
329
+ _inode_format_decorators = [(_d = t).uint16.bind(_d)];
330
+ _flags_decorators = [(_e = t).uint32.bind(_e)];
331
+ _used_bytes_decorators = [(_f = t).uint64.bind(_f)];
332
+ _total_bytes_decorators = [(_g = t).uint64.bind(_g)];
333
+ _id_decorators = [(_h = t).uint128.bind(_h)];
334
+ _metadata_block_size_decorators = [(_j = t).uint32.bind(_j)];
335
+ _metadata_offset__decorators = [(_k = t).uint32.bind(_k)];
336
+ _metadata_offset_decorators = [(_l = t).uint32.bind(_l)];
337
+ _label_decorators = [t.char(64)];
338
+ __padding_decorators = [t.char(132)];
339
+ __esDecorate(null, null, _checksum_decorators, { kind: "field", name: "checksum", static: false, private: false, access: { has: obj => "checksum" in obj, get: obj => obj.checksum, set: (obj, value) => { obj.checksum = value; } }, metadata: _metadata }, _checksum_initializers, _checksum_extraInitializers);
340
+ __esDecorate(null, null, _magic_decorators, { kind: "field", name: "magic", static: false, private: false, access: { has: obj => "magic" in obj, get: obj => obj.magic, set: (obj, value) => { obj.magic = value; } }, metadata: _metadata }, _magic_initializers, _magic_extraInitializers);
341
+ __esDecorate(null, null, _version_decorators, { kind: "field", name: "version", static: false, private: false, access: { has: obj => "version" in obj, get: obj => obj.version, set: (obj, value) => { obj.version = value; } }, metadata: _metadata }, _version_initializers, _version_extraInitializers);
342
+ __esDecorate(null, null, _inode_format_decorators, { kind: "field", name: "inode_format", static: false, private: false, access: { has: obj => "inode_format" in obj, get: obj => obj.inode_format, set: (obj, value) => { obj.inode_format = value; } }, metadata: _metadata }, _inode_format_initializers, _inode_format_extraInitializers);
343
+ __esDecorate(null, null, _flags_decorators, { kind: "field", name: "flags", static: false, private: false, access: { has: obj => "flags" in obj, get: obj => obj.flags, set: (obj, value) => { obj.flags = value; } }, metadata: _metadata }, _flags_initializers, _flags_extraInitializers);
344
+ __esDecorate(null, null, _used_bytes_decorators, { kind: "field", name: "used_bytes", static: false, private: false, access: { has: obj => "used_bytes" in obj, get: obj => obj.used_bytes, set: (obj, value) => { obj.used_bytes = value; } }, metadata: _metadata }, _used_bytes_initializers, _used_bytes_extraInitializers);
345
+ __esDecorate(null, null, _total_bytes_decorators, { kind: "field", name: "total_bytes", static: false, private: false, access: { has: obj => "total_bytes" in obj, get: obj => obj.total_bytes, set: (obj, value) => { obj.total_bytes = value; } }, metadata: _metadata }, _total_bytes_initializers, _total_bytes_extraInitializers);
346
+ __esDecorate(null, null, _id_decorators, { kind: "field", name: "id", static: false, private: false, access: { has: obj => "id" in obj, get: obj => obj.id, set: (obj, value) => { obj.id = value; } }, metadata: _metadata }, _id_initializers, _id_extraInitializers);
347
+ __esDecorate(null, null, _metadata_block_size_decorators, { kind: "field", name: "metadata_block_size", static: false, private: false, access: { has: obj => "metadata_block_size" in obj, get: obj => obj.metadata_block_size, set: (obj, value) => { obj.metadata_block_size = value; } }, metadata: _metadata }, _metadata_block_size_initializers, _metadata_block_size_extraInitializers);
348
+ __esDecorate(null, null, _metadata_offset__decorators, { kind: "field", name: "metadata_offset_", static: false, private: false, access: { has: obj => "metadata_offset_" in obj, get: obj => obj.metadata_offset_, set: (obj, value) => { obj.metadata_offset_ = value; } }, metadata: _metadata }, _metadata_offset__initializers, _metadata_offset__extraInitializers);
349
+ __esDecorate(null, null, _metadata_offset_decorators, { kind: "field", name: "metadata_offset", static: false, private: false, access: { has: obj => "metadata_offset" in obj, get: obj => obj.metadata_offset, set: (obj, value) => { obj.metadata_offset = value; } }, metadata: _metadata }, _metadata_offset_initializers, _metadata_offset_extraInitializers);
350
+ __esDecorate(null, null, _label_decorators, { kind: "field", name: "label", static: false, private: false, access: { has: obj => "label" in obj, get: obj => obj.label, set: (obj, value) => { obj.label = value; } }, metadata: _metadata }, _label_initializers, _label_extraInitializers);
351
+ __esDecorate(null, null, __padding_decorators, { kind: "field", name: "_padding", static: false, private: false, access: { has: obj => "_padding" in obj, get: obj => obj._padding, set: (obj, value) => { obj._padding = value; } }, metadata: _metadata }, __padding_initializers, __padding_extraInitializers);
352
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
353
+ SuperBlock = _classThis = _classDescriptor.value;
354
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
355
+ __runInitializers(_classThis, _classExtraInitializers);
356
+ })();
357
+ return SuperBlock = _classThis;
358
+ })();
359
+ function checksumMatches(value) {
360
+ const buffer = serialize(value);
361
+ const computed = crc32c(buffer.subarray(4)); // note we don't include the checksum when computing a new one.
362
+ return value.checksum === computed;
363
+ }
364
+ /**
365
+ *
366
+ * @category Stores and Transactions
367
+ */
368
+ export class SingleBufferStore {
369
+ constructor(buffer) {
370
+ this.flags = [];
371
+ this.name = 'sbfs';
372
+ this.id = 0x73626673; // 'sbfs'
373
+ if (buffer.byteLength < sizeof(SuperBlock) + sizeof(MetadataBlock))
374
+ throw crit(new ErrnoError(Errno.EINVAL, 'SingleBuffer: Buffer is too small for a file system.'));
375
+ this._view = !ArrayBuffer.isView(buffer) ? new DataView(buffer) : new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
376
+ this._buffer = !ArrayBuffer.isView(buffer) ? new Uint8Array(buffer) : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
377
+ this.superblock = new SuperBlock(this);
378
+ }
379
+ /**
380
+ * Update a block's checksum and write it to the store's buffer.
381
+ * @internal @hidden
382
+ */
383
+ _write(value) {
384
+ value.checksum = crc32c(serialize(value).subarray(4));
385
+ const offset = 'offset' in value ? value.offset : 0;
386
+ this._buffer.set(serialize(value), offset);
387
+ }
388
+ keys() {
389
+ const keys = new Set();
390
+ for (let block = this.superblock.metadata; block; block = block.previous) {
391
+ for (const entry of block.entries)
392
+ if (entry.offset)
393
+ keys.add(entry.id);
394
+ }
395
+ return keys;
396
+ }
397
+ get(id) {
398
+ for (let block = this.superblock.metadata; block; block = block.previous) {
399
+ for (const entry of block.entries) {
400
+ if (entry.offset && entry.id == id) {
401
+ return this._buffer.subarray(entry.offset, entry.offset + entry.size);
402
+ }
403
+ }
404
+ }
405
+ }
406
+ set(id, data) {
407
+ for (let block = this.superblock.metadata; block; block = block.previous) {
408
+ for (const entry of block.entries) {
409
+ if (!entry.offset || entry.id != id)
410
+ continue;
411
+ if (data.length <= entry.size) {
412
+ this._buffer.set(data, entry.offset);
413
+ if (data.length < entry.size) {
414
+ entry.size = data.length;
415
+ this._write(block);
416
+ }
417
+ return;
418
+ }
419
+ if (this.superblock.isUnused(entry.offset, data.length)) {
420
+ entry.size = data.length;
421
+ this._buffer.set(data, entry.offset);
422
+ this._write(block);
423
+ return;
424
+ }
425
+ const used_bytes = Number(this.superblock.used_bytes);
426
+ for (let block = this.superblock.metadata; block; block = block.previous) {
427
+ for (const entry of block.entries) {
428
+ if (entry.offset != used_bytes)
429
+ continue;
430
+ entry.offset += data.length;
431
+ this._write(block);
432
+ break;
433
+ }
434
+ }
435
+ entry.offset = used_bytes;
436
+ entry.size = data.length;
437
+ this._buffer.set(data, entry.offset);
438
+ this._write(block);
439
+ this.superblock.used_bytes += BigInt(data.length);
440
+ this._write(this.superblock);
441
+ return;
442
+ }
443
+ }
444
+ let entry = this.superblock.metadata.entries.find(e => !e.offset);
445
+ if (!entry) {
446
+ this.superblock.rotateMetadata();
447
+ entry = this.superblock.metadata.entries[0];
448
+ }
449
+ const offset = Number(this.superblock.used_bytes);
450
+ entry.id = id;
451
+ entry.offset = offset;
452
+ entry.size = data.length;
453
+ this._buffer.set(data, offset);
454
+ this.superblock.used_bytes += BigInt(data.length);
455
+ this._write(this.superblock.metadata);
456
+ this._write(this.superblock);
457
+ }
458
+ delete(id) {
459
+ for (let block = this.superblock.metadata; block; block = block.previous) {
460
+ for (const entry of block.entries) {
461
+ if (entry.id != id)
462
+ continue;
463
+ entry.offset = 0;
464
+ entry.size = 0;
465
+ this._write(block);
466
+ return;
467
+ }
468
+ }
469
+ }
470
+ async sync() {
471
+ return;
472
+ }
473
+ usage() {
474
+ return {
475
+ totalSpace: Number(this.superblock.total_bytes),
476
+ freeSpace: Number(this.superblock.total_bytes - this.superblock.used_bytes),
477
+ };
478
+ }
479
+ transaction() {
480
+ return new SyncMapTransaction(this);
481
+ }
482
+ }
483
+ const _SingleBuffer = {
484
+ name: 'SingleBuffer',
485
+ options: {
486
+ buffer: { type: 'object', required: true },
487
+ },
488
+ create({ buffer }) {
489
+ const fs = new StoreFS(new SingleBufferStore(buffer));
490
+ fs.checkRootSync();
491
+ return fs;
492
+ },
493
+ };
494
+ /**
495
+ * A backend that uses a single buffer for storing data
496
+ * @category Backends and Configuration
497
+ */
498
+ export const SingleBuffer = _SingleBuffer;
@@ -1,6 +1,6 @@
1
1
  import type { File } from '../../internal/file.js';
2
2
  import { Index } from '../../internal/file_index.js';
3
- import type { CreationOptions, PureCreationOptions } from '../../internal/filesystem.js';
3
+ import type { CreationOptions, PureCreationOptions, UsageInfo } from '../../internal/filesystem.js';
4
4
  import { FileSystem } from '../../internal/filesystem.js';
5
5
  import { Inode, type InodeLike } from '../../internal/inode.js';
6
6
  import type { Stats } from '../../stats.js';
@@ -43,6 +43,10 @@ export declare class StoreFS<T extends Store = Store> extends FileSystem {
43
43
  protected _initialized: boolean;
44
44
  ready(): Promise<void>;
45
45
  constructor(store: T);
46
+ /**
47
+ * @experimental
48
+ */
49
+ usage(): UsageInfo;
46
50
  /**
47
51
  * Delete all contents stored in the file system.
48
52
  * @deprecated
@@ -150,6 +150,16 @@ export class StoreFS extends FileSystem {
150
150
  store._fs = this;
151
151
  debug(this.name + ': supports features: ' + ((_b = this.store.flags) === null || _b === void 0 ? void 0 : _b.join(', ')));
152
152
  }
153
+ /**
154
+ * @experimental
155
+ */
156
+ usage() {
157
+ var _a, _b;
158
+ return (((_b = (_a = this.store).usage) === null || _b === void 0 ? void 0 : _b.call(_a)) || {
159
+ totalSpace: 0,
160
+ freeSpace: 0,
161
+ });
162
+ }
153
163
  /* node:coverage disable */
154
164
  /**
155
165
  * Delete all contents stored in the file system.
@@ -1,6 +1,7 @@
1
1
  import { Resource } from 'utilium/cache.js';
2
2
  import '../../polyfills.js';
3
3
  import type { StoreFS } from './fs.js';
4
+ import type { UsageInfo } from '../../internal/filesystem.js';
4
5
  /**
5
6
  * @category Stores and Transactions
6
7
  */
@@ -48,6 +49,10 @@ export interface Store {
48
49
  * Use for optimizations
49
50
  */
50
51
  readonly flags?: readonly StoreFlag[];
52
+ /**
53
+ * Usage information for the store
54
+ */
55
+ usage?(): UsageInfo;
51
56
  /**
52
57
  * @internal @hidden
53
58
  */
@@ -244,7 +244,9 @@ export class DeviceFS extends StoreFS {
244
244
  debug('Added default devices.');
245
245
  }
246
246
  constructor() {
247
- super(new InMemoryStore('devfs'));
247
+ // Please don't store your temporary files in /dev.
248
+ // If you do, you'll have up to 16 MiB
249
+ super(new InMemoryStore(0x1000000, 'devfs'));
248
250
  this.devices = new Map();
249
251
  }
250
252
  async rename(oldPath, newPath) {
@@ -1,11 +1,13 @@
1
1
  import type { InodeLike } from './inode.js';
2
2
  import { Inode } from './inode.js';
3
+ import type { UsageInfo } from './filesystem.js';
3
4
  /**
4
5
  * An Index in JSON form
5
6
  * @internal
6
7
  */
7
8
  export interface IndexData {
8
9
  version: number;
10
+ maxSize?: number;
9
11
  entries: Record<string, InodeLike>;
10
12
  }
11
13
  export declare const version = 1;
@@ -15,6 +17,7 @@ export declare const version = 1;
15
17
  * @internal
16
18
  */
17
19
  export declare class Index extends Map<string, Inode> {
20
+ maxSize: number;
18
21
  /**
19
22
  * Converts the index to JSON
20
23
  */
@@ -23,6 +26,11 @@ export declare class Index extends Map<string, Inode> {
23
26
  * Converts the index to a string
24
27
  */
25
28
  toString(): string;
29
+ /**
30
+ * Get the size in bytes of the index (including the size reported for each entry)
31
+ */
32
+ get byteSize(): number;
33
+ usage(): UsageInfo;
26
34
  pathOf(id: number): string | undefined;
27
35
  getByID(id: number): Inode | undefined;
28
36
  entryByID(id: number): {
@@ -1,9 +1,9 @@
1
1
  /* Note: this file is named file_index.ts because Typescript has special behavior regarding index.ts which can't be disabled. */
2
- import { isJSON, randomInt } from 'utilium';
2
+ import { isJSON, randomInt, sizeof } from 'utilium';
3
3
  import { S_IFDIR, S_IFMT, size_max } from '../vfs/constants.js';
4
4
  import { basename, dirname } from '../vfs/path.js';
5
5
  import { Errno, ErrnoError } from './error.js';
6
- import { Inode } from './inode.js';
6
+ import { __inode_sz, Inode } from './inode.js';
7
7
  export const version = 1;
8
8
  /**
9
9
  * An index of file metadata
@@ -11,12 +11,17 @@ export const version = 1;
11
11
  * @internal
12
12
  */
13
13
  export class Index extends Map {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.maxSize = size_max;
17
+ }
14
18
  /**
15
19
  * Converts the index to JSON
16
20
  */
17
21
  toJSON() {
18
22
  return {
19
23
  version,
24
+ maxSize: this.maxSize,
20
25
  entries: Object.fromEntries([...this].map(([k, v]) => [k, v.toJSON()])),
21
26
  };
22
27
  }
@@ -26,6 +31,21 @@ export class Index extends Map {
26
31
  toString() {
27
32
  return JSON.stringify(this.toJSON());
28
33
  }
34
+ /**
35
+ * Get the size in bytes of the index (including the size reported for each entry)
36
+ */
37
+ get byteSize() {
38
+ let size = this.size * __inode_sz;
39
+ for (const entry of this.values())
40
+ size += entry.size;
41
+ return size;
42
+ }
43
+ usage() {
44
+ return {
45
+ totalSpace: this.maxSize,
46
+ freeSpace: this.maxSize - this.byteSize,
47
+ };
48
+ }
29
49
  pathOf(id) {
30
50
  for (const [path, inode] of this) {
31
51
  if (inode.ino == id || inode.data == id)
@@ -1,7 +1,7 @@
1
1
  import { Stats } from '../stats.js';
2
2
  import { type File } from './file.js';
3
3
  import { Index } from './file_index.js';
4
- import { FileSystem, type CreationOptions, type PureCreationOptions } from './filesystem.js';
4
+ import { FileSystem, type CreationOptions, type PureCreationOptions, type UsageInfo } from './filesystem.js';
5
5
  import { Inode, type InodeLike } from './inode.js';
6
6
  /**
7
7
  * A file system that uses an `Index` for metadata.
@@ -11,6 +11,7 @@ import { Inode, type InodeLike } from './inode.js';
11
11
  export declare abstract class IndexFS extends FileSystem {
12
12
  readonly index: Index;
13
13
  constructor(id: number, name: string, index?: Index);
14
+ usage(): UsageInfo;
14
15
  /**
15
16
  * @deprecated
16
17
  */
@@ -18,6 +18,9 @@ export class IndexFS extends FileSystem {
18
18
  super(id, name);
19
19
  this.index = index;
20
20
  }
21
+ usage() {
22
+ return this.index.usage();
23
+ }
21
24
  /* node:coverage disable */
22
25
  /**
23
26
  * @deprecated
@@ -21,6 +21,14 @@ export interface InodeLike extends StatsLike<number>, InodeFields {
21
21
  * @internal @hidden
22
22
  */
23
23
  export declare const _inode_fields: readonly ["ino", "data", "size", "mode", "flags", "nlink", "uid", "gid", "atimeMs", "birthtimeMs", "mtimeMs", "ctimeMs"];
24
+ /**
25
+ * Represents which version of the `Inode` format we are on.
26
+ * 1. 58 bytes. The first member was called `ino` but used as the ID for data.
27
+ * 2. 66 bytes. Renamed the first member from `ino` to `data` and added a separate `ino` field
28
+ * 3. (current) 72 bytes. Changed the ID fields from 64 to 32 bits and added `flags`.
29
+ * @internal @hidden
30
+ */
31
+ export declare const _inode_version = 3;
24
32
  /**
25
33
  * Generic inode definition that can easily be serialized.
26
34
  * @category Internals
@@ -49,6 +49,14 @@ export const rootIno = 0;
49
49
  * @internal @hidden
50
50
  */
51
51
  export const _inode_fields = ['ino', 'data', 'size', 'mode', 'flags', 'nlink', 'uid', 'gid', 'atimeMs', 'birthtimeMs', 'mtimeMs', 'ctimeMs'];
52
+ /**
53
+ * Represents which version of the `Inode` format we are on.
54
+ * 1. 58 bytes. The first member was called `ino` but used as the ID for data.
55
+ * 2. 66 bytes. Renamed the first member from `ino` to `data` and added a separate `ino` field
56
+ * 3. (current) 72 bytes. Changed the ID fields from 64 to 32 bits and added `flags`.
57
+ * @internal @hidden
58
+ */
59
+ export const _inode_version = 3;
52
60
  /**
53
61
  * Generic inode definition that can easily be serialized.
54
62
  * @category Internals
package/dist/stats.js CHANGED
@@ -233,11 +233,11 @@ export class BigIntStats extends StatsCommon {
233
233
  * @internal
234
234
  */
235
235
  export function isStatsEqual(left, right) {
236
- return (left.size == right.size &&
237
- +left.atime == +right.atime &&
238
- +left.mtime == +right.mtime &&
239
- +left.ctime == +right.ctime &&
240
- left.mode == right.mode);
236
+ return (left.size == right.size
237
+ && +left.atime == +right.atime
238
+ && +left.mtime == +right.mtime
239
+ && +left.ctime == +right.ctime
240
+ && left.mode == right.mode);
241
241
  }
242
242
  /** @internal */
243
243
  export const ZenFsType = 0x7a656e6673; // 'z' 'e' 'n' 'f' 's'
package/dist/vfs/path.js CHANGED
@@ -338,12 +338,12 @@ export function extname(path) {
338
338
  preDotState = -1;
339
339
  }
340
340
  }
341
- if (startDot === -1 ||
342
- end === -1 ||
341
+ if (startDot === -1
342
+ || end === -1
343
343
  // We saw a non-dot character immediately before the dot
344
- preDotState === 0 ||
344
+ || preDotState === 0
345
345
  // The (right-most) trimmed path component is exactly '..'
346
- (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) {
346
+ || (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) {
347
347
  return '';
348
348
  }
349
349
  return path.slice(startDot, end);
@@ -403,11 +403,11 @@ export function parse(path) {
403
403
  }
404
404
  if (end !== -1) {
405
405
  const start = startPart === 0 && isAbsolute ? 1 : startPart;
406
- if (startDot === -1 ||
406
+ if (startDot === -1
407
407
  // We saw a non-dot character immediately before the dot
408
- preDotState === 0 ||
408
+ || preDotState === 0
409
409
  // The (right-most) trimmed path component is exactly '..'
410
- (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) {
410
+ || (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) {
411
411
  ret.base = ret.name = path.slice(start, end);
412
412
  }
413
413
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "1.9.5",
3
+ "version": "1.10.0",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -70,7 +70,7 @@
70
70
  "buffer": "^6.0.3",
71
71
  "eventemitter3": "^5.0.1",
72
72
  "readable-stream": "^4.5.2",
73
- "utilium": "^1.2.10"
73
+ "utilium": "^1.3.1"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@eslint/js": "^9.8.0",
@@ -0,0 +1,9 @@
1
+ import { SingleBuffer, configureSingle } from '../../dist/index.js';
2
+ import { copySync, data } from '../setup.js';
3
+
4
+ await configureSingle({
5
+ backend: SingleBuffer,
6
+ buffer: new ArrayBuffer(0x100000),
7
+ });
8
+
9
+ copySync(data);