@bendyline/docblocks 1.1.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -11
- package/dist/chunk-2D7Y4IR3.js +1293 -0
- package/dist/chunk-2D7Y4IR3.js.map +1 -0
- package/dist/chunk-5MYAICRE.js +957 -0
- package/dist/chunk-5MYAICRE.js.map +1 -0
- package/dist/chunk-75UUF6C4.js +802 -0
- package/dist/chunk-75UUF6C4.js.map +1 -0
- package/dist/chunk-AEHCVWU5.js +25 -0
- package/dist/chunk-AEHCVWU5.js.map +1 -0
- package/dist/chunk-AYZ4HN5M.js +20 -0
- package/dist/chunk-AYZ4HN5M.js.map +1 -0
- package/dist/chunk-EGNDADXB.js +111 -0
- package/dist/chunk-EGNDADXB.js.map +1 -0
- package/dist/chunk-EZKJZUNJ.js +13 -0
- package/dist/chunk-EZKJZUNJ.js.map +1 -0
- package/dist/chunk-FNW556FN.js +75 -0
- package/dist/chunk-FNW556FN.js.map +1 -0
- package/dist/chunk-GCPBEMMH.js +563 -0
- package/dist/chunk-GCPBEMMH.js.map +1 -0
- package/dist/chunk-J32GU3F3.js +1664 -0
- package/dist/chunk-J32GU3F3.js.map +1 -0
- package/dist/chunk-PGR7LK6D.js +1062 -0
- package/dist/chunk-PGR7LK6D.js.map +1 -0
- package/dist/chunk-RQJRQF46.js +787 -0
- package/dist/chunk-RQJRQF46.js.map +1 -0
- package/dist/{chunk-ME76RUMR.js → chunk-RVQHY3XU.js} +15 -1
- package/dist/chunk-RVQHY3XU.js.map +1 -0
- package/dist/chunk-SI2LU2RP.js +224 -0
- package/dist/chunk-SI2LU2RP.js.map +1 -0
- package/dist/chunk-W5UUUII5.js +142 -0
- package/dist/chunk-W5UUUII5.js.map +1 -0
- package/dist/document/document-session.d.ts +127 -0
- package/dist/document/document-session.d.ts.map +1 -0
- package/dist/document/document-session.js +749 -0
- package/dist/document/document-session.js.map +1 -0
- package/dist/document/filesystem-target.d.ts +17 -0
- package/dist/document/filesystem-target.d.ts.map +1 -0
- package/dist/document/filesystem-target.js +70 -0
- package/dist/document/filesystem-target.js.map +1 -0
- package/dist/document/index.d.ts +6 -0
- package/dist/document/index.d.ts.map +1 -0
- package/dist/document/index.js +4 -0
- package/dist/document/index.js.map +1 -0
- package/dist/document/recovery-journal.d.ts +125 -0
- package/dist/document/recovery-journal.d.ts.map +1 -0
- package/dist/document/recovery-journal.js +385 -0
- package/dist/document/recovery-journal.js.map +1 -0
- package/dist/document/types.d.ts +89 -0
- package/dist/document/types.d.ts.map +1 -0
- package/dist/document/types.js +9 -0
- package/dist/document/types.js.map +1 -0
- package/dist/filesystem/commit-lock.d.ts +3 -0
- package/dist/filesystem/commit-lock.d.ts.map +1 -0
- package/dist/filesystem/commit-lock.js +20 -0
- package/dist/filesystem/commit-lock.js.map +1 -0
- package/dist/filesystem/dbk-workspace.d.ts +34 -0
- package/dist/filesystem/dbk-workspace.d.ts.map +1 -0
- package/dist/filesystem/dbk-workspace.js +85 -0
- package/dist/filesystem/dbk-workspace.js.map +1 -0
- package/dist/filesystem/electron-provider-v2.d.ts +36 -0
- package/dist/filesystem/electron-provider-v2.d.ts.map +1 -0
- package/dist/filesystem/electron-provider-v2.js +503 -0
- package/dist/filesystem/electron-provider-v2.js.map +1 -0
- package/dist/filesystem/electron-provider.d.ts +6 -3
- package/dist/filesystem/electron-provider.d.ts.map +1 -1
- package/dist/filesystem/electron-provider.js +18 -13
- package/dist/filesystem/electron-provider.js.map +1 -1
- package/dist/filesystem/electron.d.ts +4 -0
- package/dist/filesystem/electron.d.ts.map +1 -0
- package/dist/filesystem/electron.js +4 -0
- package/dist/filesystem/electron.js.map +1 -0
- package/dist/filesystem/filesystem-content-container.d.ts +1 -1
- package/dist/filesystem/filesystem-content-container.d.ts.map +1 -1
- package/dist/filesystem/filesystem-content-container.js +50 -13
- package/dist/filesystem/filesystem-content-container.js.map +1 -1
- package/dist/filesystem/fs-error.d.ts +46 -0
- package/dist/filesystem/fs-error.d.ts.map +1 -0
- package/dist/filesystem/fs-error.js +170 -0
- package/dist/filesystem/fs-error.js.map +1 -0
- package/dist/filesystem/index.d.ts +15 -1
- package/dist/filesystem/index.d.ts.map +1 -1
- package/dist/filesystem/index.js +13 -0
- package/dist/filesystem/index.js.map +1 -1
- package/dist/filesystem/indexeddb-content-container.d.ts +3 -1
- package/dist/filesystem/indexeddb-content-container.d.ts.map +1 -1
- package/dist/filesystem/indexeddb-content-container.js +18 -11
- package/dist/filesystem/indexeddb-content-container.js.map +1 -1
- package/dist/filesystem/indexeddb-provider-v2.d.ts +96 -0
- package/dist/filesystem/indexeddb-provider-v2.d.ts.map +1 -0
- package/dist/filesystem/indexeddb-provider-v2.js +1241 -0
- package/dist/filesystem/indexeddb-provider-v2.js.map +1 -0
- package/dist/filesystem/indexeddb-provider.d.ts +21 -14
- package/dist/filesystem/indexeddb-provider.d.ts.map +1 -1
- package/dist/filesystem/indexeddb-provider.js +111 -261
- package/dist/filesystem/indexeddb-provider.js.map +1 -1
- package/dist/filesystem/indexeddb-store.d.ts +34 -0
- package/dist/filesystem/indexeddb-store.d.ts.map +1 -0
- package/dist/filesystem/indexeddb-store.js +165 -0
- package/dist/filesystem/indexeddb-store.js.map +1 -0
- package/dist/filesystem/indexeddb.d.ts +5 -0
- package/dist/filesystem/indexeddb.d.ts.map +1 -0
- package/dist/filesystem/indexeddb.js +5 -0
- package/dist/filesystem/indexeddb.js.map +1 -0
- package/dist/filesystem/legacy-v2-adapter.d.ts +62 -0
- package/dist/filesystem/legacy-v2-adapter.d.ts.map +1 -0
- package/dist/filesystem/legacy-v2-adapter.js +499 -0
- package/dist/filesystem/legacy-v2-adapter.js.map +1 -0
- package/dist/filesystem/memory-provider-v2.d.ts +92 -0
- package/dist/filesystem/memory-provider-v2.d.ts.map +1 -0
- package/dist/filesystem/memory-provider-v2.js +615 -0
- package/dist/filesystem/memory-provider-v2.js.map +1 -0
- package/dist/filesystem/memory-provider.d.ts +54 -0
- package/dist/filesystem/memory-provider.d.ts.map +1 -0
- package/dist/filesystem/memory-provider.js +132 -0
- package/dist/filesystem/memory-provider.js.map +1 -0
- package/dist/filesystem/memory.d.ts +4 -0
- package/dist/filesystem/memory.d.ts.map +1 -0
- package/dist/filesystem/memory.js +4 -0
- package/dist/filesystem/memory.js.map +1 -0
- package/dist/filesystem/move-entry.d.ts +9 -0
- package/dist/filesystem/move-entry.d.ts.map +1 -0
- package/dist/filesystem/move-entry.js +103 -0
- package/dist/filesystem/move-entry.js.map +1 -0
- package/dist/filesystem/move-error.d.ts +53 -0
- package/dist/filesystem/move-error.d.ts.map +1 -0
- package/dist/filesystem/move-error.js +99 -0
- package/dist/filesystem/move-error.js.map +1 -0
- package/dist/filesystem/native-provider-v2.d.ts +75 -0
- package/dist/filesystem/native-provider-v2.d.ts.map +1 -0
- package/dist/filesystem/native-provider-v2.js +849 -0
- package/dist/filesystem/native-provider-v2.js.map +1 -0
- package/dist/filesystem/native-provider.d.ts +9 -1
- package/dist/filesystem/native-provider.d.ts.map +1 -1
- package/dist/filesystem/native-provider.js +172 -37
- package/dist/filesystem/native-provider.js.map +1 -1
- package/dist/filesystem/native.d.ts +4 -0
- package/dist/filesystem/native.d.ts.map +1 -0
- package/dist/filesystem/native.js +4 -0
- package/dist/filesystem/native.js.map +1 -0
- package/dist/filesystem/types.d.ts +37 -4
- package/dist/filesystem/types.d.ts.map +1 -1
- package/dist/filesystem/types.js +11 -3
- package/dist/filesystem/types.js.map +1 -1
- package/dist/filesystem/v2.d.ts +172 -0
- package/dist/filesystem/v2.d.ts.map +1 -0
- package/dist/filesystem/v2.js +16 -0
- package/dist/filesystem/v2.js.map +1 -0
- package/dist/filesystem/workspace-path.d.ts +21 -0
- package/dist/filesystem/workspace-path.d.ts.map +1 -0
- package/dist/filesystem/workspace-path.js +80 -0
- package/dist/filesystem/workspace-path.js.map +1 -0
- package/dist/host/filesystem-v2.d.ts +45 -0
- package/dist/host/filesystem-v2.d.ts.map +1 -0
- package/dist/host/filesystem-v2.js +13 -0
- package/dist/host/filesystem-v2.js.map +1 -0
- package/dist/host/git.d.ts +200 -0
- package/dist/host/git.d.ts.map +1 -0
- package/dist/host/git.js +15 -0
- package/dist/host/git.js.map +1 -0
- package/dist/host/index.d.ts +4 -1
- package/dist/host/index.d.ts.map +1 -1
- package/dist/host/index.js +2 -0
- package/dist/host/index.js.map +1 -1
- package/dist/host/types.d.ts +114 -31
- package/dist/host/types.d.ts.map +1 -1
- package/dist/host/wire-policy.d.ts +22 -0
- package/dist/host/wire-policy.d.ts.map +1 -0
- package/dist/host/wire-policy.js +92 -0
- package/dist/host/wire-policy.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +4 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/types.d.ts +466 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +25 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/mcp/wire.d.ts +29 -0
- package/dist/mcp/wire.d.ts.map +1 -0
- package/dist/mcp/wire.js +1046 -0
- package/dist/mcp/wire.js.map +1 -0
- package/dist/mcp/zod.d.ts +5763 -0
- package/dist/mcp/zod.d.ts.map +1 -0
- package/dist/mcp/zod.js +908 -0
- package/dist/mcp/zod.js.map +1 -0
- package/dist/types-CX893hgH.d.ts +468 -0
- package/dist/types-CsX6EN2t.d.ts +301 -0
- package/dist/types-eOlt9J7n.d.ts +478 -0
- package/dist/vscode/index.d.ts +3 -0
- package/dist/vscode/index.d.ts.map +1 -0
- package/dist/vscode/index.js +3 -0
- package/dist/vscode/index.js.map +1 -0
- package/dist/vscode/messages.d.ts +204 -0
- package/dist/vscode/messages.d.ts.map +1 -0
- package/dist/vscode/messages.js +494 -0
- package/dist/vscode/messages.js.map +1 -0
- package/dist/workspace/index.d.ts +3 -2
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +2 -1
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/reconcile-electron.d.ts +19 -0
- package/dist/workspace/reconcile-electron.d.ts.map +1 -0
- package/dist/workspace/reconcile-electron.js +42 -0
- package/dist/workspace/reconcile-electron.js.map +1 -0
- package/dist/workspace/types.d.ts +41 -1
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/types.js +0 -4
- package/dist/workspace/types.js.map +1 -1
- package/dist/workspace/workspace-manager.d.ts +17 -5
- package/dist/workspace/workspace-manager.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.js +55 -11
- package/dist/workspace/workspace-manager.js.map +1 -1
- package/dist/workspace-path-CWVrcuPL.d.ts +21 -0
- package/package.json +43 -2
- package/src/document/document-session.ts +834 -0
- package/src/document/filesystem-target.ts +109 -0
- package/src/document/index.ts +41 -0
- package/src/document/recovery-journal.ts +536 -0
- package/src/document/types.ts +116 -0
- package/src/filesystem/commit-lock.ts +29 -0
- package/src/filesystem/dbk-workspace.ts +135 -0
- package/src/filesystem/electron-provider-v2.ts +630 -0
- package/src/filesystem/electron-provider.ts +25 -14
- package/src/filesystem/electron.ts +3 -0
- package/src/filesystem/filesystem-content-container.ts +53 -15
- package/src/filesystem/fs-error.ts +234 -0
- package/src/filesystem/index.ts +119 -0
- package/src/filesystem/indexeddb-content-container.ts +20 -12
- package/src/filesystem/indexeddb-provider-v2.ts +1740 -0
- package/src/filesystem/indexeddb-provider.ts +126 -300
- package/src/filesystem/indexeddb-store.ts +207 -0
- package/src/filesystem/indexeddb.ts +12 -0
- package/src/filesystem/legacy-v2-adapter.ts +779 -0
- package/src/filesystem/memory-provider-v2.ts +896 -0
- package/src/filesystem/memory-provider.ts +180 -0
- package/src/filesystem/memory.ts +15 -0
- package/src/filesystem/move-entry.ts +142 -0
- package/src/filesystem/move-error.ts +159 -0
- package/src/filesystem/native-provider-v2.ts +1165 -0
- package/src/filesystem/native-provider.ts +194 -41
- package/src/filesystem/native.ts +16 -0
- package/src/filesystem/types.ts +49 -4
- package/src/filesystem/v2.ts +231 -0
- package/src/filesystem/workspace-path.ts +92 -0
- package/src/host/filesystem-v2.ts +104 -0
- package/src/host/git.ts +246 -0
- package/src/host/index.ts +46 -0
- package/src/host/types.ts +155 -34
- package/src/host/wire-policy.ts +103 -0
- package/src/index.ts +2 -1
- package/src/mcp/index.ts +3 -0
- package/src/mcp/types.ts +594 -0
- package/src/mcp/wire.ts +1280 -0
- package/src/mcp/zod.ts +1000 -0
- package/src/vscode/index.ts +2 -0
- package/src/vscode/messages.ts +704 -0
- package/src/workspace/index.ts +8 -1
- package/src/workspace/reconcile-electron.ts +65 -0
- package/src/workspace/types.ts +39 -1
- package/src/workspace/workspace-manager.ts +71 -10
- package/dist/chunk-GUNM43XZ.js +0 -63
- package/dist/chunk-GUNM43XZ.js.map +0 -1
- package/dist/chunk-IM7LCQJ6.js +0 -850
- package/dist/chunk-IM7LCQJ6.js.map +0 -1
- package/dist/chunk-ME76RUMR.js.map +0 -1
- package/dist/index-pgBHBQ9G.d.ts +0 -213
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ElectronFileSystemProvider — implements FileSystemProvider by delegating
|
|
3
3
|
* to the Electron desktop host's fs IPC bridge. Every operation is scoped
|
|
4
|
-
* to an
|
|
5
|
-
*
|
|
4
|
+
* to an opaque workspace id that the main process resolves against its
|
|
5
|
+
* registered workspace authority.
|
|
6
6
|
*
|
|
7
7
|
* This file has no Electron dependency — it is a pure IPC client that
|
|
8
8
|
* relies on the `docBlocksHost` global installed by the preload script.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { FileSystemProvider, FileSystemEntry, FileMeta } from './types.js';
|
|
11
|
+
import type { FileCommitResult, FileSystemProvider, FileSystemEntry, FileMeta } from './types.js';
|
|
12
12
|
import { maybeGetDocBlocksHost } from '../host/index.js';
|
|
13
13
|
import type { DocBlocksHostFsAPI } from '../host/types.js';
|
|
14
|
+
import { ElectronFileSystemProviderV2 } from './electron-provider-v2.js';
|
|
14
15
|
|
|
15
16
|
export { isElectronHost } from '../host/index.js';
|
|
16
17
|
|
|
@@ -27,6 +28,7 @@ function getHostFs(): DocBlocksHostFsAPI {
|
|
|
27
28
|
export class ElectronFileSystemProvider implements FileSystemProvider {
|
|
28
29
|
readonly id: string;
|
|
29
30
|
readonly label: string;
|
|
31
|
+
readonly v2: ElectronFileSystemProviderV2;
|
|
30
32
|
|
|
31
33
|
private readonly rootPath: string;
|
|
32
34
|
|
|
@@ -34,6 +36,7 @@ export class ElectronFileSystemProvider implements FileSystemProvider {
|
|
|
34
36
|
this.id = id;
|
|
35
37
|
this.label = label;
|
|
36
38
|
this.rootPath = rootPath;
|
|
39
|
+
this.v2 = new ElectronFileSystemProviderV2(id, label, rootPath);
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
/** Absolute path this provider is rooted at. */
|
|
@@ -42,47 +45,55 @@ export class ElectronFileSystemProvider implements FileSystemProvider {
|
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
readFile(path: string): Promise<string | null> {
|
|
45
|
-
return getHostFs().readFile(this.
|
|
48
|
+
return getHostFs().readFile(this.id, path);
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
writeFile(path: string, content: string): Promise<void> {
|
|
49
|
-
return getHostFs().writeFile(this.
|
|
52
|
+
return getHostFs().writeFile(this.id, path, content);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
commitFile(
|
|
56
|
+
path: string,
|
|
57
|
+
content: string,
|
|
58
|
+
expectedContent: string | null,
|
|
59
|
+
): Promise<FileCommitResult> {
|
|
60
|
+
return getHostFs().commitFile(this.id, path, content, expectedContent);
|
|
50
61
|
}
|
|
51
62
|
|
|
52
63
|
delete(path: string): Promise<void> {
|
|
53
|
-
return getHostFs().delete(this.
|
|
64
|
+
return getHostFs().delete(this.id, path);
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
rename(oldPath: string, newPath: string): Promise<void> {
|
|
57
|
-
return getHostFs().rename(this.
|
|
68
|
+
return getHostFs().rename(this.id, oldPath, newPath);
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
readDirectory(path: string): Promise<FileSystemEntry[]> {
|
|
61
|
-
return getHostFs().readDirectory(this.
|
|
72
|
+
return getHostFs().readDirectory(this.id, path);
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
exists(path: string): Promise<boolean> {
|
|
65
|
-
return getHostFs().exists(this.
|
|
76
|
+
return getHostFs().exists(this.id, path);
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
createDirectory(path: string): Promise<void> {
|
|
69
|
-
return getHostFs().createDirectory(this.
|
|
80
|
+
return getHostFs().createDirectory(this.id, path);
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
stat(path: string): Promise<FileMeta | null> {
|
|
73
|
-
return getHostFs().stat(this.
|
|
84
|
+
return getHostFs().stat(this.id, path);
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
readBinary(path: string): Promise<ArrayBuffer | null> {
|
|
77
|
-
return getHostFs().readBinary(this.
|
|
88
|
+
return getHostFs().readBinary(this.id, path);
|
|
78
89
|
}
|
|
79
90
|
|
|
80
91
|
writeBinary(path: string, data: ArrayBuffer | Uint8Array): Promise<void> {
|
|
81
|
-
return getHostFs().writeBinary(this.
|
|
92
|
+
return getHostFs().writeBinary(this.id, path, data);
|
|
82
93
|
}
|
|
83
94
|
|
|
84
95
|
/** Subscribe to external change notifications under this root. */
|
|
85
96
|
watch(onChange: (changedPath: string) => void): () => void {
|
|
86
|
-
return getHostFs().watch(this.
|
|
97
|
+
return getHostFs().watch(this.id, onChange);
|
|
87
98
|
}
|
|
88
99
|
}
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
import type { ContentContainer, ContentEntry } from '@bendyline/squisq/storage';
|
|
11
11
|
import { findDocumentPath } from '@bendyline/squisq/storage';
|
|
12
|
-
import
|
|
12
|
+
import { FsError } from './fs-error.js';
|
|
13
|
+
import { getFileSystemProviderV2, type FileSystemProvider } from './types.js';
|
|
14
|
+
import { parseWorkspacePath, workspacePathContains, workspacePathJoin } from './workspace-path.js';
|
|
13
15
|
|
|
14
16
|
const EXTENSION_MIME_MAP: Record<string, string> = {
|
|
15
17
|
'.md': 'text/markdown',
|
|
@@ -37,8 +39,7 @@ function guessMimeType(path: string): string {
|
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
function joinPrefix(prefix: string, p: string): string {
|
|
40
|
-
|
|
41
|
-
return prefix.replace(/\/+$/, '') + '/' + clean;
|
|
42
|
+
return workspacePathJoin(parseWorkspacePath(prefix), p);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export class FileSystemContentContainer implements ContentContainer {
|
|
@@ -48,11 +49,13 @@ export class FileSystemContentContainer implements ContentContainer {
|
|
|
48
49
|
private readonly provider: FileSystemProvider,
|
|
49
50
|
prefix = '.docblocks/media',
|
|
50
51
|
) {
|
|
51
|
-
this.prefix = prefix
|
|
52
|
+
this.prefix = parseWorkspacePath(prefix);
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
async readFile(path: string): Promise<ArrayBuffer | null> {
|
|
55
56
|
const full = joinPrefix(this.prefix, path);
|
|
57
|
+
const providerV2 = getFileSystemProviderV2(this.provider);
|
|
58
|
+
if (providerV2) return (await providerV2.readFile(parseWorkspacePath(full)))?.data ?? null;
|
|
56
59
|
const binary = await this.provider.readBinary(full);
|
|
57
60
|
if (binary) return binary;
|
|
58
61
|
// IndexedDB-backed workspaces store text via `writeFile(string)` and
|
|
@@ -67,43 +70,78 @@ export class FileSystemContentContainer implements ContentContainer {
|
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
async writeFile(path: string, data: ArrayBuffer | Uint8Array, _mimeType?: string): Promise<void> {
|
|
70
|
-
|
|
73
|
+
const full = joinPrefix(this.prefix, path);
|
|
74
|
+
const providerV2 = getFileSystemProviderV2(this.provider);
|
|
75
|
+
if (providerV2) {
|
|
76
|
+
await providerV2.writeFile(parseWorkspacePath(full), data, {
|
|
77
|
+
mode: 'upsert',
|
|
78
|
+
createParents: true,
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
await this.provider.writeBinary(full, data);
|
|
71
83
|
}
|
|
72
84
|
|
|
73
85
|
async removeFile(path: string): Promise<void> {
|
|
74
|
-
|
|
86
|
+
const full = joinPrefix(this.prefix, path);
|
|
87
|
+
const providerV2 = getFileSystemProviderV2(this.provider);
|
|
88
|
+
if (providerV2) {
|
|
89
|
+
await providerV2.remove(parseWorkspacePath(full), { missing: 'ignore' });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
await this.provider.delete(full);
|
|
75
93
|
}
|
|
76
94
|
|
|
77
95
|
async listFiles(prefix?: string): Promise<ContentEntry[]> {
|
|
78
96
|
const entries: ContentEntry[] = [];
|
|
97
|
+
const providerV2 = getFileSystemProviderV2(this.provider);
|
|
98
|
+
const root = parseWorkspacePath(this.prefix);
|
|
99
|
+
type ListedEntry = {
|
|
100
|
+
readonly kind: 'file' | 'directory';
|
|
101
|
+
readonly path: string;
|
|
102
|
+
readonly size?: number | null;
|
|
103
|
+
};
|
|
79
104
|
const walk = async (dir: string) => {
|
|
80
|
-
let children;
|
|
105
|
+
let children: readonly ListedEntry[];
|
|
81
106
|
try {
|
|
82
|
-
children =
|
|
83
|
-
|
|
84
|
-
|
|
107
|
+
children = providerV2
|
|
108
|
+
? await providerV2.readDirectory(parseWorkspacePath(dir))
|
|
109
|
+
: await this.provider.readDirectory(dir);
|
|
110
|
+
} catch (error: unknown) {
|
|
111
|
+
// An absent container root means an empty container. Every other
|
|
112
|
+
// storage fault (including a subtree disappearing mid-snapshot)
|
|
113
|
+
// remains observable to backup/export callers.
|
|
114
|
+
if (dir === root && error instanceof FsError && error.code === 'not-found') return;
|
|
115
|
+
throw error;
|
|
85
116
|
}
|
|
86
117
|
for (const child of children) {
|
|
87
118
|
if (child.kind === 'directory') {
|
|
88
119
|
await walk(child.path);
|
|
89
120
|
} else {
|
|
90
|
-
const
|
|
121
|
+
const childPath = parseWorkspacePath(child.path);
|
|
122
|
+
const prefixPath = parseWorkspacePath(this.prefix);
|
|
123
|
+
if (!workspacePathContains(prefixPath, childPath)) continue;
|
|
124
|
+
const rel = childPath === prefixPath ? '' : childPath.slice(prefixPath.length + 1);
|
|
91
125
|
if (prefix && !rel.startsWith(prefix)) continue;
|
|
92
|
-
const
|
|
126
|
+
const size = child.size ?? (await this.provider.stat(child.path))?.size ?? 0;
|
|
93
127
|
entries.push({
|
|
94
128
|
path: rel,
|
|
95
129
|
mimeType: guessMimeType(rel),
|
|
96
|
-
size
|
|
130
|
+
size,
|
|
97
131
|
});
|
|
98
132
|
}
|
|
99
133
|
}
|
|
100
134
|
};
|
|
101
|
-
await walk(
|
|
135
|
+
await walk(root);
|
|
102
136
|
return entries;
|
|
103
137
|
}
|
|
104
138
|
|
|
105
139
|
async exists(path: string): Promise<boolean> {
|
|
106
|
-
|
|
140
|
+
const full = joinPrefix(this.prefix, path);
|
|
141
|
+
const providerV2 = getFileSystemProviderV2(this.provider);
|
|
142
|
+
return providerV2
|
|
143
|
+
? (await providerV2.stat(parseWorkspacePath(full))) !== null
|
|
144
|
+
: this.provider.exists(full);
|
|
107
145
|
}
|
|
108
146
|
|
|
109
147
|
async getDocumentPath(): Promise<string | null> {
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/** Stable error codes shared by every filesystem transport and backend. */
|
|
2
|
+
export type FsErrorCode =
|
|
3
|
+
| 'invalid-path'
|
|
4
|
+
| 'path-escape'
|
|
5
|
+
| 'not-found'
|
|
6
|
+
| 'already-exists'
|
|
7
|
+
| 'type-mismatch'
|
|
8
|
+
| 'not-empty'
|
|
9
|
+
| 'permission-denied'
|
|
10
|
+
| 'conflict'
|
|
11
|
+
| 'quota-exceeded'
|
|
12
|
+
| 'not-supported'
|
|
13
|
+
| 'busy'
|
|
14
|
+
| 'disposed'
|
|
15
|
+
| 'closed'
|
|
16
|
+
| 'aborted'
|
|
17
|
+
| 'io';
|
|
18
|
+
|
|
19
|
+
export type FsOperation =
|
|
20
|
+
| 'parse-path'
|
|
21
|
+
| 'read'
|
|
22
|
+
| 'stat'
|
|
23
|
+
| 'list'
|
|
24
|
+
| 'write'
|
|
25
|
+
| 'create-directory'
|
|
26
|
+
| 'remove'
|
|
27
|
+
| 'move'
|
|
28
|
+
| 'snapshot'
|
|
29
|
+
| 'watch'
|
|
30
|
+
| 'dispose';
|
|
31
|
+
|
|
32
|
+
/** Plain-data form safe to send over IPC, postMessage, or JSON. */
|
|
33
|
+
export interface SerializedFsError {
|
|
34
|
+
readonly name: 'FsError';
|
|
35
|
+
readonly code: FsErrorCode;
|
|
36
|
+
readonly message: string;
|
|
37
|
+
readonly operation: FsOperation | null;
|
|
38
|
+
readonly path: string | null;
|
|
39
|
+
readonly destinationPath: string | null;
|
|
40
|
+
readonly retryable: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface FsErrorContext {
|
|
44
|
+
readonly operation?: FsOperation;
|
|
45
|
+
readonly path?: string;
|
|
46
|
+
readonly destinationPath?: string;
|
|
47
|
+
readonly retryable?: boolean;
|
|
48
|
+
readonly defaultCode?: FsErrorCode;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const FS_ERROR_CODES: ReadonlySet<string> = new Set<FsErrorCode>([
|
|
52
|
+
'invalid-path',
|
|
53
|
+
'path-escape',
|
|
54
|
+
'not-found',
|
|
55
|
+
'already-exists',
|
|
56
|
+
'type-mismatch',
|
|
57
|
+
'not-empty',
|
|
58
|
+
'permission-denied',
|
|
59
|
+
'conflict',
|
|
60
|
+
'quota-exceeded',
|
|
61
|
+
'not-supported',
|
|
62
|
+
'busy',
|
|
63
|
+
'disposed',
|
|
64
|
+
'closed',
|
|
65
|
+
'aborted',
|
|
66
|
+
'io',
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
const FS_OPERATIONS: ReadonlySet<string> = new Set<FsOperation>([
|
|
70
|
+
'parse-path',
|
|
71
|
+
'read',
|
|
72
|
+
'stat',
|
|
73
|
+
'list',
|
|
74
|
+
'write',
|
|
75
|
+
'create-directory',
|
|
76
|
+
'remove',
|
|
77
|
+
'move',
|
|
78
|
+
'snapshot',
|
|
79
|
+
'watch',
|
|
80
|
+
'dispose',
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
/** Error with a stable code and a deliberately serializable public shape. */
|
|
84
|
+
export class FsError extends Error {
|
|
85
|
+
public readonly code: FsErrorCode;
|
|
86
|
+
public readonly operation: FsOperation | null;
|
|
87
|
+
public readonly path: string | null;
|
|
88
|
+
public readonly destinationPath: string | null;
|
|
89
|
+
public readonly retryable: boolean;
|
|
90
|
+
|
|
91
|
+
public constructor(code: FsErrorCode, message: string, context: FsErrorContext = {}) {
|
|
92
|
+
super(message);
|
|
93
|
+
this.name = 'FsError';
|
|
94
|
+
this.code = code;
|
|
95
|
+
this.operation = context.operation ?? null;
|
|
96
|
+
this.path = context.path ?? null;
|
|
97
|
+
this.destinationPath = context.destinationPath ?? null;
|
|
98
|
+
this.retryable = context.retryable ?? (code === 'io' || code === 'aborted' || code === 'busy');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public toJSON(): SerializedFsError {
|
|
102
|
+
return {
|
|
103
|
+
name: 'FsError',
|
|
104
|
+
code: this.code,
|
|
105
|
+
message: this.message,
|
|
106
|
+
operation: this.operation,
|
|
107
|
+
path: this.path,
|
|
108
|
+
destinationPath: this.destinationPath,
|
|
109
|
+
retryable: this.retryable,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function mapDomExceptionToFsErrorCode(name: string): FsErrorCode {
|
|
115
|
+
switch (name) {
|
|
116
|
+
case 'NotFoundError':
|
|
117
|
+
return 'not-found';
|
|
118
|
+
case 'ConstraintError':
|
|
119
|
+
return 'already-exists';
|
|
120
|
+
case 'TypeMismatchError':
|
|
121
|
+
return 'type-mismatch';
|
|
122
|
+
case 'InvalidModificationError':
|
|
123
|
+
return 'not-empty';
|
|
124
|
+
case 'NotAllowedError':
|
|
125
|
+
case 'SecurityError':
|
|
126
|
+
case 'NoModificationAllowedError':
|
|
127
|
+
return 'permission-denied';
|
|
128
|
+
case 'QuotaExceededError':
|
|
129
|
+
return 'quota-exceeded';
|
|
130
|
+
case 'NotSupportedError':
|
|
131
|
+
return 'not-supported';
|
|
132
|
+
case 'InvalidStateError':
|
|
133
|
+
return 'disposed';
|
|
134
|
+
case 'AbortError':
|
|
135
|
+
return 'aborted';
|
|
136
|
+
default:
|
|
137
|
+
return 'io';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function mapNodeErrorCodeToFsErrorCode(code: string): FsErrorCode {
|
|
142
|
+
switch (code) {
|
|
143
|
+
case 'ENOENT':
|
|
144
|
+
return 'not-found';
|
|
145
|
+
case 'ENOTDIR':
|
|
146
|
+
return 'type-mismatch';
|
|
147
|
+
case 'EEXIST':
|
|
148
|
+
return 'already-exists';
|
|
149
|
+
case 'EISDIR':
|
|
150
|
+
return 'type-mismatch';
|
|
151
|
+
case 'ENOTEMPTY':
|
|
152
|
+
return 'not-empty';
|
|
153
|
+
case 'EACCES':
|
|
154
|
+
case 'EPERM':
|
|
155
|
+
return 'permission-denied';
|
|
156
|
+
case 'ENOSPC':
|
|
157
|
+
case 'EDQUOT':
|
|
158
|
+
return 'quota-exceeded';
|
|
159
|
+
case 'EBUSY':
|
|
160
|
+
return 'busy';
|
|
161
|
+
case 'EXDEV':
|
|
162
|
+
case 'ENOTSUP':
|
|
163
|
+
case 'EOPNOTSUPP':
|
|
164
|
+
return 'not-supported';
|
|
165
|
+
default:
|
|
166
|
+
return 'io';
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function isSerializedFsError(value: unknown): value is SerializedFsError {
|
|
171
|
+
if (!value || typeof value !== 'object') return false;
|
|
172
|
+
const candidate = value as Partial<Record<keyof SerializedFsError, unknown>>;
|
|
173
|
+
return (
|
|
174
|
+
candidate.name === 'FsError' &&
|
|
175
|
+
typeof candidate.code === 'string' &&
|
|
176
|
+
FS_ERROR_CODES.has(candidate.code) &&
|
|
177
|
+
typeof candidate.message === 'string' &&
|
|
178
|
+
(candidate.operation === null ||
|
|
179
|
+
(typeof candidate.operation === 'string' && FS_OPERATIONS.has(candidate.operation))) &&
|
|
180
|
+
(candidate.path === null || typeof candidate.path === 'string') &&
|
|
181
|
+
(candidate.destinationPath === null || typeof candidate.destinationPath === 'string') &&
|
|
182
|
+
typeof candidate.retryable === 'boolean'
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function deserializeFsError(value: SerializedFsError): FsError {
|
|
187
|
+
return new FsError(value.code, value.message, {
|
|
188
|
+
operation: value.operation ?? undefined,
|
|
189
|
+
path: value.path ?? undefined,
|
|
190
|
+
destinationPath: value.destinationPath ?? undefined,
|
|
191
|
+
retryable: value.retryable,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function fsErrorFromUnknown(error: unknown, context: FsErrorContext = {}): FsError {
|
|
196
|
+
if (error instanceof FsError) return error;
|
|
197
|
+
if (isSerializedFsError(error)) return deserializeFsError(error);
|
|
198
|
+
|
|
199
|
+
const name = getErrorStringField(error, 'name');
|
|
200
|
+
const nodeCode = getErrorStringField(error, 'code');
|
|
201
|
+
const message = getErrorStringField(error, 'message') ?? String(error);
|
|
202
|
+
const code =
|
|
203
|
+
context.defaultCode ??
|
|
204
|
+
(nodeCode
|
|
205
|
+
? mapNodeErrorCodeToFsErrorCode(nodeCode)
|
|
206
|
+
: name
|
|
207
|
+
? mapDomExceptionToFsErrorCode(name)
|
|
208
|
+
: 'io');
|
|
209
|
+
return new FsError(code, message, context);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function serializeFsError(error: unknown, context: FsErrorContext = {}): SerializedFsError {
|
|
213
|
+
return fsErrorFromUnknown(error, context).toJSON();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* True when a storage backend rejected an operation because the origin or
|
|
218
|
+
* device is out of space. Covers typed `FsError`s (`'quota-exceeded'`, from
|
|
219
|
+
* DOMException `QuotaExceededError` or Node `ENOSPC`/`EDQUOT`), serialized
|
|
220
|
+
* transport copies, and raw DOMExceptions from backends that do not wrap
|
|
221
|
+
* their errors (e.g. the File System Access API) — including cross-realm
|
|
222
|
+
* instances where `instanceof DOMException` would fail.
|
|
223
|
+
*/
|
|
224
|
+
export function isQuotaExceededError(error: unknown): boolean {
|
|
225
|
+
if (error instanceof FsError) return error.code === 'quota-exceeded';
|
|
226
|
+
if (isSerializedFsError(error)) return error.code === 'quota-exceeded';
|
|
227
|
+
return getErrorStringField(error, 'name') === 'QuotaExceededError';
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function getErrorStringField(error: unknown, key: 'name' | 'message' | 'code'): string | null {
|
|
231
|
+
if (!error || typeof error !== 'object') return null;
|
|
232
|
+
const value = (error as Record<string, unknown>)[key];
|
|
233
|
+
return typeof value === 'string' ? value : null;
|
|
234
|
+
}
|
package/src/filesystem/index.ts
CHANGED
|
@@ -1,15 +1,127 @@
|
|
|
1
1
|
export type {
|
|
2
2
|
FileSystemProvider,
|
|
3
|
+
FileSystemProviderWithV2,
|
|
3
4
|
FileSystemEntry,
|
|
4
5
|
FileEntry,
|
|
5
6
|
FolderEntry,
|
|
6
7
|
FileMeta,
|
|
8
|
+
FileCommitResult,
|
|
7
9
|
} from './types.js';
|
|
10
|
+
export { getFileSystemProviderV2, hasFileSystemProviderV2 } from './types.js';
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
WORKSPACE_ROOT,
|
|
14
|
+
parseWorkspacePath,
|
|
15
|
+
tryParseWorkspacePath,
|
|
16
|
+
workspacePathToLegacy,
|
|
17
|
+
workspacePathJoin,
|
|
18
|
+
workspacePathDirname,
|
|
19
|
+
workspacePathBasename,
|
|
20
|
+
workspacePathContains,
|
|
21
|
+
type WorkspacePath,
|
|
22
|
+
} from './workspace-path.js';
|
|
23
|
+
export {
|
|
24
|
+
FsError,
|
|
25
|
+
mapDomExceptionToFsErrorCode,
|
|
26
|
+
mapNodeErrorCodeToFsErrorCode,
|
|
27
|
+
isSerializedFsError,
|
|
28
|
+
isQuotaExceededError,
|
|
29
|
+
serializeFsError,
|
|
30
|
+
deserializeFsError,
|
|
31
|
+
fsErrorFromUnknown,
|
|
32
|
+
type FsErrorCode,
|
|
33
|
+
type FsOperation,
|
|
34
|
+
type SerializedFsError,
|
|
35
|
+
type FsErrorContext,
|
|
36
|
+
} from './fs-error.js';
|
|
37
|
+
export type {
|
|
38
|
+
FileSystemProviderV2,
|
|
39
|
+
FileSystemProviderCapabilities,
|
|
40
|
+
FileSystemVersion,
|
|
41
|
+
FileSystemConditionalWriteStrength,
|
|
42
|
+
FileSystemSymlinkPolicy,
|
|
43
|
+
FileSystemAtomicity,
|
|
44
|
+
FileSystemCaseSensitivity,
|
|
45
|
+
FileSystemDurability,
|
|
46
|
+
FileSystemEntrySnapshot,
|
|
47
|
+
FileSystemFileSnapshot,
|
|
48
|
+
FileSystemDirectorySnapshot,
|
|
49
|
+
FileSystemFileRead,
|
|
50
|
+
FileSystemSnapshot,
|
|
51
|
+
FileSystemSnapshotEntry,
|
|
52
|
+
FileSystemSnapshotFile,
|
|
53
|
+
FileSystemWriteMode,
|
|
54
|
+
FileSystemWriteOptions,
|
|
55
|
+
FileSystemCreateDirectoryMode,
|
|
56
|
+
FileSystemCreateDirectoryOptions,
|
|
57
|
+
FileSystemRemoveOptions,
|
|
58
|
+
FileSystemRemoveResult,
|
|
59
|
+
FileSystemMoveOptions,
|
|
60
|
+
FileSystemWatchEventType,
|
|
61
|
+
FileSystemWatchEventOrigin,
|
|
62
|
+
FileSystemWatchEvent,
|
|
63
|
+
FileSystemEntryWatchEvent,
|
|
64
|
+
FileSystemOverflowWatchEvent,
|
|
65
|
+
FileSystemWatchListener,
|
|
66
|
+
FileSystemWatchErrorListener,
|
|
67
|
+
FileSystemWatchOptions,
|
|
68
|
+
FileSystemWatchSubscription,
|
|
69
|
+
} from './v2.js';
|
|
70
|
+
export { parseFileSystemVersion, tryParseFileSystemVersion } from './v2.js';
|
|
71
|
+
export {
|
|
72
|
+
MemoryFileSystemProviderV2,
|
|
73
|
+
type MemoryFilePayloadKind,
|
|
74
|
+
type MemoryFileSystemV2CompatibilityFile,
|
|
75
|
+
type MemoryFileSystemV2Replacement,
|
|
76
|
+
type MemoryFileSystemV2ReplacementFile,
|
|
77
|
+
type MemoryFileSystemV2State,
|
|
78
|
+
type MemoryFileSystemV2StateFile,
|
|
79
|
+
} from './memory-provider-v2.js';
|
|
80
|
+
export {
|
|
81
|
+
IndexedDBFileSystemProviderV2,
|
|
82
|
+
type IndexedDBFileSystemProviderV2Options,
|
|
83
|
+
type IndexedDBLegacyConflictReason,
|
|
84
|
+
type IndexedDBLegacyConflictResolution,
|
|
85
|
+
type IndexedDBLegacyMigrationConflict,
|
|
86
|
+
type IndexedDBLegacyRecoveryCandidate,
|
|
87
|
+
type IndexedDBLegacyRecoveryKind,
|
|
88
|
+
} from './indexeddb-provider-v2.js';
|
|
89
|
+
export {
|
|
90
|
+
LegacyFileSystemProviderV2Adapter,
|
|
91
|
+
type LegacyFilePayloadModel,
|
|
92
|
+
type LegacyFileSystemProviderV2AdapterOptions,
|
|
93
|
+
} from './legacy-v2-adapter.js';
|
|
8
94
|
|
|
9
95
|
export { IndexedDBFileSystemProvider } from './indexeddb-provider.js';
|
|
96
|
+
export {
|
|
97
|
+
MemoryFileSystemProvider,
|
|
98
|
+
type MemoryFileSystemSnapshot,
|
|
99
|
+
type MemoryFileSystemSnapshotFile,
|
|
100
|
+
} from './memory-provider.js';
|
|
101
|
+
export {
|
|
102
|
+
createDbkWorkspaceSnapshot,
|
|
103
|
+
replaceMemoryWorkspaceFromDbk,
|
|
104
|
+
type DbkWorkspaceAssetLayout,
|
|
105
|
+
type DbkWorkspaceSnapshot,
|
|
106
|
+
type DbkWorkspaceSnapshotOptions,
|
|
107
|
+
} from './dbk-workspace.js';
|
|
10
108
|
export { IndexedDBContentContainer } from './indexeddb-content-container.js';
|
|
11
109
|
export { FileSystemContentContainer } from './filesystem-content-container.js';
|
|
12
110
|
export { createFileMediaProvider } from './file-media-provider.js';
|
|
111
|
+
export { documentCompanionPath, moveFileSystemEntry } from './move-entry.js';
|
|
112
|
+
export {
|
|
113
|
+
FileSystemMoveRecoveryError,
|
|
114
|
+
FileSystemPartialMoveError,
|
|
115
|
+
isFileSystemMoveRecoveryError,
|
|
116
|
+
isFileSystemMoveStateError,
|
|
117
|
+
isFileSystemPartialMoveError,
|
|
118
|
+
describeEntryMove,
|
|
119
|
+
describePartialMove,
|
|
120
|
+
type FileSystemEntryMoveState,
|
|
121
|
+
type FileSystemMoveLocation,
|
|
122
|
+
type FileSystemPartialMoveState,
|
|
123
|
+
type FileSystemPathPresence,
|
|
124
|
+
} from './move-error.js';
|
|
13
125
|
|
|
14
126
|
export {
|
|
15
127
|
NativeFileSystemProvider,
|
|
@@ -20,5 +132,12 @@ export {
|
|
|
20
132
|
loadDirectoryHandle,
|
|
21
133
|
removeDirectoryHandle,
|
|
22
134
|
} from './native-provider.js';
|
|
135
|
+
export {
|
|
136
|
+
NativeFileSystemProviderV2,
|
|
137
|
+
NativeFileSystemMoveRecoveryError,
|
|
138
|
+
type NativeFileSystemMovePathState,
|
|
139
|
+
type NativeFileSystemMoveRecoveryState,
|
|
140
|
+
} from './native-provider-v2.js';
|
|
23
141
|
|
|
24
142
|
export { ElectronFileSystemProvider, isElectronHost } from './electron-provider.js';
|
|
143
|
+
export { ElectronFileSystemProviderV2 } from './electron-provider-v2.js';
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ContentContainer, ContentEntry } from '@bendyline/squisq/storage';
|
|
9
9
|
import { findDocumentPath } from '@bendyline/squisq/storage';
|
|
10
|
-
import {
|
|
10
|
+
import { IndexedDBFileSystemProviderV2 } from './indexeddb-provider-v2.js';
|
|
11
|
+
import { parseWorkspacePath, WORKSPACE_ROOT } from './workspace-path.js';
|
|
11
12
|
|
|
12
13
|
// ── MIME type guessing ─────────────────────────────────────────────
|
|
13
14
|
|
|
@@ -39,49 +40,51 @@ function guessMimeType(path: string): string {
|
|
|
39
40
|
// ── Implementation ─────────────────────────────────────────────────
|
|
40
41
|
|
|
41
42
|
export class IndexedDBContentContainer implements ContentContainer {
|
|
42
|
-
private provider:
|
|
43
|
+
private readonly provider: IndexedDBFileSystemProviderV2;
|
|
43
44
|
|
|
44
45
|
constructor(workspaceId: string) {
|
|
45
|
-
this.provider = new
|
|
46
|
+
this.provider = new IndexedDBFileSystemProviderV2(`${workspaceId}-media`, 'Media Storage');
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
async readFile(path: string): Promise<ArrayBuffer | null> {
|
|
49
|
-
return this.provider.
|
|
50
|
+
return (await this.provider.readFile(parseWorkspacePath(path)))?.data ?? null;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
async writeFile(path: string, data: ArrayBuffer | Uint8Array, _mimeType?: string): Promise<void> {
|
|
53
|
-
await this.provider.
|
|
54
|
+
await this.provider.writeFile(parseWorkspacePath(path), data, {
|
|
55
|
+
mode: 'upsert',
|
|
56
|
+
createParents: true,
|
|
57
|
+
});
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
async removeFile(path: string): Promise<void> {
|
|
57
|
-
await this.provider.
|
|
61
|
+
await this.provider.remove(parseWorkspacePath(path), { missing: 'ignore' });
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
async listFiles(prefix?: string): Promise<ContentEntry[]> {
|
|
61
65
|
const entries: ContentEntry[] = [];
|
|
62
66
|
const walk = async (dir: string) => {
|
|
63
|
-
const children = await this.provider.readDirectory(dir);
|
|
67
|
+
const children = await this.provider.readDirectory(parseWorkspacePath(dir));
|
|
64
68
|
for (const child of children) {
|
|
65
69
|
if (child.kind === 'directory') {
|
|
66
70
|
await walk(child.path);
|
|
67
71
|
} else {
|
|
68
|
-
const filePath = child.path
|
|
72
|
+
const filePath = child.path;
|
|
69
73
|
if (prefix && !filePath.startsWith(prefix)) continue;
|
|
70
|
-
const meta = await this.provider.stat(child.path);
|
|
71
74
|
entries.push({
|
|
72
75
|
path: filePath,
|
|
73
76
|
mimeType: guessMimeType(filePath),
|
|
74
|
-
size:
|
|
77
|
+
size: child.size,
|
|
75
78
|
});
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
};
|
|
79
|
-
await walk(
|
|
82
|
+
await walk(WORKSPACE_ROOT);
|
|
80
83
|
return entries;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
async exists(path: string): Promise<boolean> {
|
|
84
|
-
return this.provider.
|
|
87
|
+
return (await this.provider.stat(parseWorkspacePath(path))) !== null;
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
async getDocumentPath(): Promise<string | null> {
|
|
@@ -101,4 +104,9 @@ export class IndexedDBContentContainer implements ContentContainer {
|
|
|
101
104
|
const data = new TextEncoder().encode(markdown);
|
|
102
105
|
await this.writeFile(name, data, 'text/markdown');
|
|
103
106
|
}
|
|
107
|
+
|
|
108
|
+
/** Release the IndexedDB connection when a long-lived container is retired. */
|
|
109
|
+
async dispose(): Promise<void> {
|
|
110
|
+
await this.provider.dispose();
|
|
111
|
+
}
|
|
104
112
|
}
|