vfs-kit 1.0.1 → 1.0.2

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.
Files changed (61) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +145 -35
  3. package/dist/VfsAdapter-BWjniD9Y.d.mts +57 -0
  4. package/dist/VfsAdapter-DOBt_TyL.d.ts +57 -0
  5. package/dist/VfsEngine-B6nhgyjQ.d.mts +152 -0
  6. package/dist/VfsEngine-DLx0iUpi.d.ts +152 -0
  7. package/dist/VfsNode-D10gxL5W.d.mts +48 -0
  8. package/dist/VfsNode-D10gxL5W.d.ts +48 -0
  9. package/dist/adapters/index.d.mts +201 -0
  10. package/dist/adapters/index.d.ts +201 -0
  11. package/dist/adapters/index.js +1159 -0
  12. package/dist/adapters/index.js.map +1 -0
  13. package/dist/adapters/index.mjs +1159 -0
  14. package/dist/adapters/index.mjs.map +1 -0
  15. package/dist/chunk-2FEJBM4N.js +60 -0
  16. package/dist/chunk-2FEJBM4N.js.map +1 -0
  17. package/dist/chunk-7OQI6PNM.mjs +60 -0
  18. package/dist/chunk-7OQI6PNM.mjs.map +1 -0
  19. package/dist/chunk-ALWOZGZI.mjs +23 -0
  20. package/dist/chunk-ALWOZGZI.mjs.map +1 -0
  21. package/dist/chunk-POSVS4C7.mjs +531 -0
  22. package/dist/chunk-POSVS4C7.mjs.map +1 -0
  23. package/dist/chunk-R3ROYAMW.js +23 -0
  24. package/dist/chunk-R3ROYAMW.js.map +1 -0
  25. package/dist/chunk-SWRBVSS6.mjs +16 -0
  26. package/dist/chunk-SWRBVSS6.mjs.map +1 -0
  27. package/dist/chunk-U2CKTXY7.js +16 -0
  28. package/dist/chunk-U2CKTXY7.js.map +1 -0
  29. package/dist/chunk-WZVVI3HX.js +531 -0
  30. package/dist/chunk-WZVVI3HX.js.map +1 -0
  31. package/dist/components/index.d.mts +193 -0
  32. package/dist/components/index.d.ts +193 -0
  33. package/dist/components/index.js +1197 -0
  34. package/dist/components/index.js.map +1 -0
  35. package/dist/components/index.mjs +1197 -0
  36. package/dist/components/index.mjs.map +1 -0
  37. package/dist/hooks/index.d.mts +120 -0
  38. package/dist/hooks/index.d.ts +120 -0
  39. package/dist/hooks/index.js +51 -0
  40. package/dist/hooks/index.js.map +1 -0
  41. package/dist/hooks/index.mjs +51 -0
  42. package/dist/hooks/index.mjs.map +1 -0
  43. package/dist/index.d.mts +42 -0
  44. package/dist/index.d.ts +38 -3
  45. package/dist/index.js +528 -13
  46. package/dist/index.js.map +1 -0
  47. package/dist/index.mjs +530 -0
  48. package/dist/index.mjs.map +1 -0
  49. package/dist/useVfsTabs-ZHDaLrM1.d.mts +39 -0
  50. package/dist/useVfsTabs-ZHDaLrM1.d.ts +39 -0
  51. package/package.json +59 -61
  52. package/dist/index.cjs +0 -43
  53. package/dist/index.d.cts +0 -7
  54. package/index.js +0 -7
  55. package/src/components/TreeView.tsx +0 -5
  56. package/src/components/index.ts +0 -1
  57. package/src/hooks/index.ts +0 -1
  58. package/src/hooks/useVfs.ts +0 -3
  59. package/src/index.ts +0 -2
  60. package/tsconfig.json +0 -44
  61. package/tsup.config.ts +0 -10
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 bengru07
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 bengru07
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,35 +1,145 @@
1
- # vfs-kit
2
- A headless-first Virtual File System for React. Complete with drag-and-drop hierarchy, tab management and pluggable storage adapters.
3
-
4
- ## Building the library
5
-
6
- ```bash
7
- npm install
8
- npm run build
9
- ```
10
-
11
- The package is authored in TypeScript and exports ESM modules with declaration files. React, Tailwind, and any Shadcn UI components are declared as
12
- peer dependencies a consuming project must provide them.
13
-
14
- ## Using locally
15
-
16
- While developing you can link the package into a demo app:
17
-
18
- ```bash
19
- cd ./*/vfs-kit-test
20
- npm link vfs-kit
21
- ```
22
-
23
- Import components in your app:
24
-
25
- ```tsx
26
- import { TreeView, useVfs } from "vfs-kit";
27
- ```
28
-
29
- ## Notes
30
-
31
- The current release only includes a simple placeholder component. Real
32
- Shadcn imports can be added once you’re ready; you may need to supply a
33
- local `declare module '@shadcn/ui';` or update tsconfig `moduleResolution` to
34
- `nodenext`.
35
-
1
+ # vfs-kit Explorer — React + TypeScript + Vite
2
+
3
+ A dual-pane file explorer built on top of `vfs-kit`, demonstrating a fully custom UI layer over a virtual filesystem engine. Features tabbed editing, drag-and-drop file tree, and per-file snapshot history.
4
+
5
+ ---
6
+
7
+ ## Features
8
+
9
+ - **Dual-pane layout** — two independent `FilePane` instances (`Source` / `Target`), each with its own workspace, file tree, and tab strip
10
+ - **Custom file tree** — built with `FileTree` + a fully custom `renderNode` prop; supports drag-and-drop reordering, inline rename (`InlineInput`), folder expand/collapse, and drop-zone overlays
11
+ - **Tabbed editor** uses `TabListBase` (not `TabList`) so tabs share a single `useVfsTabs` instance with the rest of the pane; tabs support dirty indicators, lock/unlock, drag-to-reorder, and close
12
+ - **Per-file snapshot history** `useFileHistory` hook manages snapshots per open file using only the public `fs` / `tree` surfaces from `useVfsEngine`; the `HistoryPanel` sidebar lets you label, take, restore, and remove snapshots
13
+ - **Clean tab switching** — `Editor` is keyed on `node.id` so React fully remounts it on tab change, resetting both content state and history without any manual sync
14
+
15
+ ---
16
+
17
+ ## Architecture
18
+
19
+ ### `FilePane`
20
+
21
+ The top-level pane component. Owns the single `useVfsTabs` instance for its workspace and passes it directly into `TabListBase` — this is important because `TabList` (the higher-level component) would create its own internal `useVfsTabs` instance, resulting in two disconnected reducer states.
22
+
23
+ ```
24
+ FilePane
25
+ ├── FileTree (sidebar) ← custom renderNode wired to tabs.open on file click
26
+ ├── TabListBase (tab strip) ← driven by the same tabs instance as FileTree
27
+ └── FileRenderer (editor area)
28
+ └── Editor (keyed on node.id)
29
+ ├── textarea
30
+ ├── useFileHistory
31
+ └── HistoryPanel
32
+ ```
33
+
34
+ ### `useFileHistory`
35
+
36
+ Per-tab in-memory snapshot manager. Scoped to the currently open file — history is intentionally discarded when the tab closes.
37
+
38
+ ```ts
39
+ const { snapshots, takeSnapshot, restore, remove, loading } = useFileHistory({
40
+ workspaceId: 'ws-a',
41
+ nodeId: 'some-file-id',
42
+ onRestore: (bytes) => setContent(new TextDecoder().decode(bytes)),
43
+ });
44
+ ```
45
+
46
+ - `takeSnapshot(label?)` — calls `fs.snapshot`, then re-fetches via `tree.getSnapshots` to update the list immediately
47
+ - `restore(index)` — fetches snapshot content, writes it back via `fs.write`, then calls `onRestore` so the editor syncs its controlled state
48
+ - `remove(index)` — local-state removal only; `deleteSnapshot` is not yet a formal `VfsCommand` (see [Roadmap](#roadmap))
49
+
50
+ Uses only the public `fs` / `tree` surfaces — no direct `engine` access.
51
+
52
+ ### `Editor`
53
+
54
+ Extracted from `FilePane` and keyed on `node.id`. Owns its own `content` state initialised from `initialContent`, and delegates history entirely to `useFileHistory`. The `setContentRef` pattern ensures `onRestore` never captures a stale setter across re-renders.
55
+
56
+ ### `HistoryPanel`
57
+
58
+ Collapsible sidebar rendered inside `Editor`. Shows snapshots most-recent first with timestamp and byte size. Each entry has restore (↺) and delete (🗑) actions. The label input submits on Enter or the `+` button.
59
+
60
+ ---
61
+
62
+ ## Key Implementation Notes
63
+
64
+ ### Why `TabListBase` instead of `TabList`
65
+
66
+ `TabList` calls `useVfsTabs` internally, producing a second isolated reducer. `TabListBase` accepts the tab state as props, so the single instance created in `FilePane` is the source of truth for both the tree's `tabs.open()` calls and the tab strip's render.
67
+
68
+ ### Why `Editor` is keyed on `node.id`
69
+
70
+ Without a key, switching tabs leaves the previous file's content in state. Keying causes React to fully unmount and remount `Editor`, which resets `content`, `snapshots`, and `loading` cleanly — no `useEffect` sync required.
71
+
72
+ ### Why `tree` is omitted from `useEffect` deps in `useFileHistory`
73
+
74
+ `useVfsEngine` builds `tree` via an immediately-invoked `useCallback` (`useCallback(...)()`) so a new object reference is produced on every render. Including it in deps would cause an infinite reload loop. `nodeId` is the correct and only trigger.
75
+
76
+ ---
77
+
78
+ ## Roadmap
79
+
80
+ - **`deleteSnapshot` as a formal command** — add `{ op: 'deleteSnapshot', fileId, index }` to `VfsCommand` in `VfsEngine`, wire it through `runSnapshot`, expose it on `VfsFsApi` as `fs.deleteSnapshot(fileId, index)`, then replace the local-only removal in `useFileHistory.remove` with that call
81
+ - **Autosave snapshots** — `VfsEngineConfig` already has `history.autosave` — hook up a `setInterval` in `useFileHistory` when `autosave.enabled` is true
82
+ - **Snapshot diff view** — compare snapshot content against current file content before restoring
83
+
84
+ ---
85
+
86
+ ## Vite / ESLint setup
87
+
88
+ This project was scaffolded from the official React + TypeScript + Vite template.
89
+
90
+ ### Available plugins
91
+
92
+ - [`@vitejs/plugin-react`](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) — uses Babel (or oxc with rolldown-vite) for Fast Refresh
93
+ - [`@vitejs/plugin-react-swc`](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) — uses SWC for Fast Refresh
94
+
95
+ ### React Compiler
96
+
97
+ Not enabled by default due to dev/build performance impact. See the [React Compiler installation docs](https://react.dev/learn/react-compiler/installation) to add it.
98
+
99
+ ### Type-aware ESLint rules
100
+
101
+ For production apps, enable type-aware lint rules:
102
+
103
+ ```js
104
+ export default defineConfig([
105
+ globalIgnores(['dist']),
106
+ {
107
+ files: ['**/*.{ts,tsx}'],
108
+ extends: [
109
+ tseslint.configs.recommendedTypeChecked,
110
+ // or tseslint.configs.strictTypeChecked for stricter rules
111
+ tseslint.configs.stylisticTypeChecked,
112
+ ],
113
+ languageOptions: {
114
+ parserOptions: {
115
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
116
+ tsconfigRootDir: import.meta.dirname,
117
+ },
118
+ },
119
+ },
120
+ ])
121
+ ```
122
+
123
+ You can also add React-specific lint rules:
124
+
125
+ ```js
126
+ import reactX from 'eslint-plugin-react-x'
127
+ import reactDom from 'eslint-plugin-react-dom'
128
+
129
+ export default defineConfig([
130
+ globalIgnores(['dist']),
131
+ {
132
+ files: ['**/*.{ts,tsx}'],
133
+ extends: [
134
+ reactX.configs['recommended-typescript'],
135
+ reactDom.configs.recommended,
136
+ ],
137
+ languageOptions: {
138
+ parserOptions: {
139
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
140
+ tsconfigRootDir: import.meta.dirname,
141
+ },
142
+ },
143
+ },
144
+ ])
145
+ ```
@@ -0,0 +1,57 @@
1
+ import { e as VfsNode, b as VfsFileNode, d as VfsFolderNode, f as VfsNodeOrder, c as VfsFileSnapshot, g as VfsOperation } from './VfsNode-D10gxL5W.mjs';
2
+
3
+ interface VfsChange {
4
+ op: VfsOperation;
5
+ nodeIds: string[];
6
+ timestamp: number;
7
+ }
8
+ interface VfsSearchOptions {
9
+ scope?: string | null;
10
+ kind?: "file" | "folder";
11
+ includeTrashed?: boolean;
12
+ }
13
+ interface VfsCreateFileParams {
14
+ parentId: string | null;
15
+ name: string;
16
+ mimeType?: string;
17
+ meta?: Record<string, unknown>;
18
+ }
19
+ interface VfsCreateFolderParams {
20
+ parentId: string | null;
21
+ name: string;
22
+ meta?: Record<string, unknown>;
23
+ }
24
+ declare abstract class VfsAdapter<TMeta = Record<string, unknown>> {
25
+ abstract readonly supportsHistory: boolean;
26
+ abstract generateId(): string;
27
+ abstract getNodes(ids: string[]): Promise<VfsNode<TMeta>[]>;
28
+ abstract getNodeById(id: string): Promise<VfsNode<TMeta> | null>;
29
+ abstract getNodeByPath(path: string): Promise<VfsNode<TMeta> | null>;
30
+ abstract getChildren(parentId: string | null, options?: {
31
+ includeTrashed?: boolean;
32
+ }): Promise<VfsNode<TMeta>[]>;
33
+ abstract getTrashed(): Promise<VfsNode<TMeta>[]>;
34
+ abstract search(query: string, options?: VfsSearchOptions): Promise<VfsNode<TMeta>[]>;
35
+ abstract readFile(id: string): Promise<Uint8Array>;
36
+ abstract writeFile(id: string, content: Uint8Array): Promise<void>;
37
+ abstract createFile(params: VfsCreateFileParams): Promise<VfsFileNode<TMeta>>;
38
+ abstract createFolder(params: VfsCreateFolderParams): Promise<VfsFolderNode<TMeta>>;
39
+ abstract updateNode(id: string, updates: Partial<VfsNode<TMeta>>): Promise<VfsNode<TMeta>>;
40
+ abstract deleteNode(id: string, permanent?: boolean): Promise<void>;
41
+ abstract deleteNodes(ids: string[], permanent?: boolean): Promise<void>;
42
+ abstract restoreNode(id: string): Promise<VfsNode<TMeta>>;
43
+ abstract purgeNode(id: string): Promise<void>;
44
+ abstract moveNode(id: string, newParentId: string | null): Promise<VfsNode<TMeta>>;
45
+ abstract moveNodes(ids: string[], newParentId: string | null): Promise<VfsNode<TMeta>[]>;
46
+ abstract lockNode(id: string, sessionId: string): Promise<VfsNode<TMeta>>;
47
+ abstract unlockNode(id: string, sessionId: string): Promise<VfsNode<TMeta>>;
48
+ abstract getOrder(parentId: string | null): Promise<VfsNodeOrder | null>;
49
+ abstract setOrder(parentId: string | null, orderedIds: string[]): Promise<void>;
50
+ getSnapshots(fileId: string): Promise<VfsFileSnapshot[]>;
51
+ saveSnapshot(fileId: string, content: Uint8Array, label?: string): Promise<VfsFileSnapshot>;
52
+ restoreSnapshot(fileId: string, index: number): Promise<void>;
53
+ deleteSnapshot(fileId: string, index: number): Promise<void>;
54
+ abstract onChanged(callback: (change: VfsChange) => void): () => void;
55
+ }
56
+
57
+ export { VfsAdapter as V, type VfsChange as a, type VfsCreateFileParams as b, type VfsCreateFolderParams as c, type VfsSearchOptions as d };
@@ -0,0 +1,57 @@
1
+ import { e as VfsNode, b as VfsFileNode, d as VfsFolderNode, f as VfsNodeOrder, c as VfsFileSnapshot, g as VfsOperation } from './VfsNode-D10gxL5W.js';
2
+
3
+ interface VfsChange {
4
+ op: VfsOperation;
5
+ nodeIds: string[];
6
+ timestamp: number;
7
+ }
8
+ interface VfsSearchOptions {
9
+ scope?: string | null;
10
+ kind?: "file" | "folder";
11
+ includeTrashed?: boolean;
12
+ }
13
+ interface VfsCreateFileParams {
14
+ parentId: string | null;
15
+ name: string;
16
+ mimeType?: string;
17
+ meta?: Record<string, unknown>;
18
+ }
19
+ interface VfsCreateFolderParams {
20
+ parentId: string | null;
21
+ name: string;
22
+ meta?: Record<string, unknown>;
23
+ }
24
+ declare abstract class VfsAdapter<TMeta = Record<string, unknown>> {
25
+ abstract readonly supportsHistory: boolean;
26
+ abstract generateId(): string;
27
+ abstract getNodes(ids: string[]): Promise<VfsNode<TMeta>[]>;
28
+ abstract getNodeById(id: string): Promise<VfsNode<TMeta> | null>;
29
+ abstract getNodeByPath(path: string): Promise<VfsNode<TMeta> | null>;
30
+ abstract getChildren(parentId: string | null, options?: {
31
+ includeTrashed?: boolean;
32
+ }): Promise<VfsNode<TMeta>[]>;
33
+ abstract getTrashed(): Promise<VfsNode<TMeta>[]>;
34
+ abstract search(query: string, options?: VfsSearchOptions): Promise<VfsNode<TMeta>[]>;
35
+ abstract readFile(id: string): Promise<Uint8Array>;
36
+ abstract writeFile(id: string, content: Uint8Array): Promise<void>;
37
+ abstract createFile(params: VfsCreateFileParams): Promise<VfsFileNode<TMeta>>;
38
+ abstract createFolder(params: VfsCreateFolderParams): Promise<VfsFolderNode<TMeta>>;
39
+ abstract updateNode(id: string, updates: Partial<VfsNode<TMeta>>): Promise<VfsNode<TMeta>>;
40
+ abstract deleteNode(id: string, permanent?: boolean): Promise<void>;
41
+ abstract deleteNodes(ids: string[], permanent?: boolean): Promise<void>;
42
+ abstract restoreNode(id: string): Promise<VfsNode<TMeta>>;
43
+ abstract purgeNode(id: string): Promise<void>;
44
+ abstract moveNode(id: string, newParentId: string | null): Promise<VfsNode<TMeta>>;
45
+ abstract moveNodes(ids: string[], newParentId: string | null): Promise<VfsNode<TMeta>[]>;
46
+ abstract lockNode(id: string, sessionId: string): Promise<VfsNode<TMeta>>;
47
+ abstract unlockNode(id: string, sessionId: string): Promise<VfsNode<TMeta>>;
48
+ abstract getOrder(parentId: string | null): Promise<VfsNodeOrder | null>;
49
+ abstract setOrder(parentId: string | null, orderedIds: string[]): Promise<void>;
50
+ getSnapshots(fileId: string): Promise<VfsFileSnapshot[]>;
51
+ saveSnapshot(fileId: string, content: Uint8Array, label?: string): Promise<VfsFileSnapshot>;
52
+ restoreSnapshot(fileId: string, index: number): Promise<void>;
53
+ deleteSnapshot(fileId: string, index: number): Promise<void>;
54
+ abstract onChanged(callback: (change: VfsChange) => void): () => void;
55
+ }
56
+
57
+ export { VfsAdapter as V, type VfsChange as a, type VfsCreateFileParams as b, type VfsCreateFolderParams as c, type VfsSearchOptions as d };
@@ -0,0 +1,152 @@
1
+ import { V as VfsAdapter, a as VfsChange } from './VfsAdapter-BWjniD9Y.mjs';
2
+ import { V as VfsEngineConfig, e as VfsNode, c as VfsFileSnapshot } from './VfsNode-D10gxL5W.mjs';
3
+
4
+ type VfsCommand = {
5
+ op: "create";
6
+ kind: "file";
7
+ parentId: string | null;
8
+ name: string;
9
+ mimeType?: string;
10
+ meta?: Record<string, unknown>;
11
+ } | {
12
+ op: "create";
13
+ kind: "folder";
14
+ parentId: string | null;
15
+ name: string;
16
+ meta?: Record<string, unknown>;
17
+ } | {
18
+ op: "rename";
19
+ id: string;
20
+ newName: string;
21
+ } | {
22
+ op: "delete";
23
+ ids: string[];
24
+ permanent?: boolean;
25
+ } | {
26
+ op: "restore";
27
+ ids: string[];
28
+ } | {
29
+ op: "purge";
30
+ ids: string[];
31
+ } | {
32
+ op: "move";
33
+ ids: string[];
34
+ newParentId: string | null;
35
+ } | {
36
+ op: "write";
37
+ id: string;
38
+ content: Uint8Array;
39
+ } | {
40
+ op: "lock";
41
+ ids: string[];
42
+ } | {
43
+ op: "unlock";
44
+ ids: string[];
45
+ } | {
46
+ op: "reorder";
47
+ parentId: string | null;
48
+ orderedIds: string[];
49
+ } | {
50
+ op: "snapshot";
51
+ fileId: string;
52
+ label?: string;
53
+ };
54
+ type VfsEventMap<TMeta> = {
55
+ created: VfsNode<TMeta>;
56
+ renamed: VfsNode<TMeta>;
57
+ deleted: {
58
+ ids: string[];
59
+ permanent: boolean;
60
+ };
61
+ restored: VfsNode<TMeta>[];
62
+ purged: {
63
+ ids: string[];
64
+ };
65
+ moved: VfsNode<TMeta>[];
66
+ written: {
67
+ id: string;
68
+ };
69
+ locked: VfsNode<TMeta>[];
70
+ unlocked: VfsNode<TMeta>[];
71
+ reordered: {
72
+ parentId: string | null;
73
+ orderedIds: string[];
74
+ };
75
+ warning: {
76
+ code: string;
77
+ tabId?: string;
78
+ nodeId?: string;
79
+ };
80
+ snapshot: VfsFileSnapshot;
81
+ pending: {
82
+ command: VfsCommand;
83
+ };
84
+ settled: {
85
+ command: VfsCommand;
86
+ success: boolean;
87
+ };
88
+ error: {
89
+ command: VfsCommand;
90
+ error: Error;
91
+ };
92
+ change: VfsChange;
93
+ };
94
+ type VfsEventListener<TMeta, K extends keyof VfsEventMap<TMeta>> = (payload: VfsEventMap<TMeta>[K]) => void;
95
+ type CacheStrategy = "eager" | "lazy" | "hybrid";
96
+ declare class VfsEngine<TMeta extends Record<string, unknown> | undefined = Record<string, unknown>> {
97
+ private adapter;
98
+ private config;
99
+ private cache;
100
+ private listeners;
101
+ private unsubscribeAdapter;
102
+ version: number;
103
+ pending: boolean;
104
+ lastError: Error | null;
105
+ constructor(adapter: VfsAdapter<TMeta>, config: Partial<VfsEngineConfig<TMeta>> & {
106
+ sessionId: string;
107
+ });
108
+ init(strategy?: CacheStrategy): Promise<void>;
109
+ destroy(): void;
110
+ subscribe(onStoreChange: () => void): () => void;
111
+ execute(command: VfsCommand): Promise<void>;
112
+ private runCommand;
113
+ private runCreate;
114
+ private runRename;
115
+ private runDelete;
116
+ private runRestore;
117
+ private runPurge;
118
+ private runMove;
119
+ private runWrite;
120
+ private runLock;
121
+ private runUnlock;
122
+ private runReorder;
123
+ private runSnapshot;
124
+ getNode(id: string): Promise<VfsNode<TMeta> | null>;
125
+ getChildren(parentId: string | null, options?: {
126
+ includeTrashed?: boolean;
127
+ }): Promise<VfsNode<TMeta>[]>;
128
+ getPath(id: string): Promise<string>;
129
+ readFile(id: string): Promise<Uint8Array>;
130
+ search(query: string, options?: {
131
+ scope?: string | null;
132
+ kind?: "file" | "folder";
133
+ includeTrashed?: boolean;
134
+ }): Promise<VfsNode<TMeta>[]>;
135
+ getTrashed(): Promise<VfsNode<TMeta>[]>;
136
+ getSnapshots(fileId: string): Promise<VfsFileSnapshot[]>;
137
+ private applySortOrder;
138
+ private autoSort;
139
+ private loadChildren;
140
+ private hydrateCache;
141
+ private cacheNode;
142
+ private evictNode;
143
+ private handleAdapterChange;
144
+ private resolveNode;
145
+ private assertNoDuplicate;
146
+ private extractTargetIds;
147
+ on<K extends keyof VfsEventMap<TMeta>>(event: K, listener: VfsEventListener<TMeta, K>): () => void;
148
+ emit<K extends keyof VfsEventMap<TMeta>>(event: K, payload: VfsEventMap<TMeta>[K]): void;
149
+ private emitChange;
150
+ }
151
+
152
+ export { VfsEngine as V, type VfsCommand as a, type VfsEventMap as b };
@@ -0,0 +1,152 @@
1
+ import { V as VfsAdapter, a as VfsChange } from './VfsAdapter-DOBt_TyL.js';
2
+ import { V as VfsEngineConfig, e as VfsNode, c as VfsFileSnapshot } from './VfsNode-D10gxL5W.js';
3
+
4
+ type VfsCommand = {
5
+ op: "create";
6
+ kind: "file";
7
+ parentId: string | null;
8
+ name: string;
9
+ mimeType?: string;
10
+ meta?: Record<string, unknown>;
11
+ } | {
12
+ op: "create";
13
+ kind: "folder";
14
+ parentId: string | null;
15
+ name: string;
16
+ meta?: Record<string, unknown>;
17
+ } | {
18
+ op: "rename";
19
+ id: string;
20
+ newName: string;
21
+ } | {
22
+ op: "delete";
23
+ ids: string[];
24
+ permanent?: boolean;
25
+ } | {
26
+ op: "restore";
27
+ ids: string[];
28
+ } | {
29
+ op: "purge";
30
+ ids: string[];
31
+ } | {
32
+ op: "move";
33
+ ids: string[];
34
+ newParentId: string | null;
35
+ } | {
36
+ op: "write";
37
+ id: string;
38
+ content: Uint8Array;
39
+ } | {
40
+ op: "lock";
41
+ ids: string[];
42
+ } | {
43
+ op: "unlock";
44
+ ids: string[];
45
+ } | {
46
+ op: "reorder";
47
+ parentId: string | null;
48
+ orderedIds: string[];
49
+ } | {
50
+ op: "snapshot";
51
+ fileId: string;
52
+ label?: string;
53
+ };
54
+ type VfsEventMap<TMeta> = {
55
+ created: VfsNode<TMeta>;
56
+ renamed: VfsNode<TMeta>;
57
+ deleted: {
58
+ ids: string[];
59
+ permanent: boolean;
60
+ };
61
+ restored: VfsNode<TMeta>[];
62
+ purged: {
63
+ ids: string[];
64
+ };
65
+ moved: VfsNode<TMeta>[];
66
+ written: {
67
+ id: string;
68
+ };
69
+ locked: VfsNode<TMeta>[];
70
+ unlocked: VfsNode<TMeta>[];
71
+ reordered: {
72
+ parentId: string | null;
73
+ orderedIds: string[];
74
+ };
75
+ warning: {
76
+ code: string;
77
+ tabId?: string;
78
+ nodeId?: string;
79
+ };
80
+ snapshot: VfsFileSnapshot;
81
+ pending: {
82
+ command: VfsCommand;
83
+ };
84
+ settled: {
85
+ command: VfsCommand;
86
+ success: boolean;
87
+ };
88
+ error: {
89
+ command: VfsCommand;
90
+ error: Error;
91
+ };
92
+ change: VfsChange;
93
+ };
94
+ type VfsEventListener<TMeta, K extends keyof VfsEventMap<TMeta>> = (payload: VfsEventMap<TMeta>[K]) => void;
95
+ type CacheStrategy = "eager" | "lazy" | "hybrid";
96
+ declare class VfsEngine<TMeta extends Record<string, unknown> | undefined = Record<string, unknown>> {
97
+ private adapter;
98
+ private config;
99
+ private cache;
100
+ private listeners;
101
+ private unsubscribeAdapter;
102
+ version: number;
103
+ pending: boolean;
104
+ lastError: Error | null;
105
+ constructor(adapter: VfsAdapter<TMeta>, config: Partial<VfsEngineConfig<TMeta>> & {
106
+ sessionId: string;
107
+ });
108
+ init(strategy?: CacheStrategy): Promise<void>;
109
+ destroy(): void;
110
+ subscribe(onStoreChange: () => void): () => void;
111
+ execute(command: VfsCommand): Promise<void>;
112
+ private runCommand;
113
+ private runCreate;
114
+ private runRename;
115
+ private runDelete;
116
+ private runRestore;
117
+ private runPurge;
118
+ private runMove;
119
+ private runWrite;
120
+ private runLock;
121
+ private runUnlock;
122
+ private runReorder;
123
+ private runSnapshot;
124
+ getNode(id: string): Promise<VfsNode<TMeta> | null>;
125
+ getChildren(parentId: string | null, options?: {
126
+ includeTrashed?: boolean;
127
+ }): Promise<VfsNode<TMeta>[]>;
128
+ getPath(id: string): Promise<string>;
129
+ readFile(id: string): Promise<Uint8Array>;
130
+ search(query: string, options?: {
131
+ scope?: string | null;
132
+ kind?: "file" | "folder";
133
+ includeTrashed?: boolean;
134
+ }): Promise<VfsNode<TMeta>[]>;
135
+ getTrashed(): Promise<VfsNode<TMeta>[]>;
136
+ getSnapshots(fileId: string): Promise<VfsFileSnapshot[]>;
137
+ private applySortOrder;
138
+ private autoSort;
139
+ private loadChildren;
140
+ private hydrateCache;
141
+ private cacheNode;
142
+ private evictNode;
143
+ private handleAdapterChange;
144
+ private resolveNode;
145
+ private assertNoDuplicate;
146
+ private extractTargetIds;
147
+ on<K extends keyof VfsEventMap<TMeta>>(event: K, listener: VfsEventListener<TMeta, K>): () => void;
148
+ emit<K extends keyof VfsEventMap<TMeta>>(event: K, payload: VfsEventMap<TMeta>[K]): void;
149
+ private emitChange;
150
+ }
151
+
152
+ export { VfsEngine as V, type VfsCommand as a, type VfsEventMap as b };