@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
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-neutral document persistence contracts.
|
|
3
|
+
*
|
|
4
|
+
* A DocumentSession owns one logical document at a time. Storage-specific
|
|
5
|
+
* adapters implement DocumentCommitTarget; the session owns revisions,
|
|
6
|
+
* serialization, lifecycle transitions, and conflict state.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { DocumentRecoveryJournal } from './recovery-journal.js';
|
|
10
|
+
|
|
11
|
+
export type DocumentSaveReason =
|
|
12
|
+
| 'autosave'
|
|
13
|
+
| 'manual'
|
|
14
|
+
| 'transition'
|
|
15
|
+
| 'close'
|
|
16
|
+
| 'backup'
|
|
17
|
+
| 'conflict';
|
|
18
|
+
|
|
19
|
+
export type DocumentExternalVersion = string | number | null;
|
|
20
|
+
|
|
21
|
+
export interface DocumentCommitRequest {
|
|
22
|
+
/** Stable key for the target this commit was created for. */
|
|
23
|
+
targetKey: string;
|
|
24
|
+
/** Latest complete document snapshot to persist. */
|
|
25
|
+
content: string;
|
|
26
|
+
/** Monotonic session revision represented by content. */
|
|
27
|
+
revision: number;
|
|
28
|
+
/** Most recent revision acknowledged by this target. */
|
|
29
|
+
persistedRevision: number;
|
|
30
|
+
/** Content last acknowledged by the target, or null if it did not exist. */
|
|
31
|
+
persistedContent: string | null;
|
|
32
|
+
reason: DocumentSaveReason;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface DocumentCommitResult {
|
|
36
|
+
/** Optional backend version (mtime, TextDocument.version, etag, and so on). */
|
|
37
|
+
version?: DocumentExternalVersion;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DocumentCommitTarget {
|
|
41
|
+
readonly key: string;
|
|
42
|
+
commit(request: DocumentCommitRequest): Promise<DocumentCommitResult | void>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type DocumentSessionStatus =
|
|
46
|
+
| 'idle'
|
|
47
|
+
| 'saved'
|
|
48
|
+
| 'dirty'
|
|
49
|
+
| 'saving'
|
|
50
|
+
| 'error'
|
|
51
|
+
| 'conflict'
|
|
52
|
+
| 'closed';
|
|
53
|
+
|
|
54
|
+
export type DocumentSessionLifecycle = 'open' | 'transitioning' | 'closing' | 'closed';
|
|
55
|
+
|
|
56
|
+
export interface DocumentExternalSnapshot {
|
|
57
|
+
/** Target key captured before the external read began. */
|
|
58
|
+
targetKey: string;
|
|
59
|
+
/** Monotonic observation number assigned by the target watcher. */
|
|
60
|
+
sequence?: number;
|
|
61
|
+
/** Null represents an externally deleted document. */
|
|
62
|
+
content: string | null;
|
|
63
|
+
version?: DocumentExternalVersion;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface DocumentSessionConflict {
|
|
67
|
+
targetKey: string;
|
|
68
|
+
localContent: string;
|
|
69
|
+
localRevision: number;
|
|
70
|
+
externalContent: string | null;
|
|
71
|
+
externalVersion: DocumentExternalVersion;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface DocumentSessionSnapshot {
|
|
75
|
+
targetKey: string | null;
|
|
76
|
+
content: string;
|
|
77
|
+
revision: number;
|
|
78
|
+
persistedRevision: number;
|
|
79
|
+
persistedContent: string | null;
|
|
80
|
+
savingRevision: number | null;
|
|
81
|
+
generation: number;
|
|
82
|
+
status: DocumentSessionStatus;
|
|
83
|
+
lifecycle: DocumentSessionLifecycle;
|
|
84
|
+
frozen: boolean;
|
|
85
|
+
error: Error | null;
|
|
86
|
+
conflict: DocumentSessionConflict | null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface DocumentSessionOptions {
|
|
90
|
+
autoSaveDelayMs?: number;
|
|
91
|
+
/** Whether edits schedule automatic persistence. Manual and lifecycle flushes remain available. */
|
|
92
|
+
autoSaveEnabled?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Bounded delays used after an automatic save fails. Each entry permits
|
|
95
|
+
* one retry; exhausting the list leaves the session in error state until
|
|
96
|
+
* the user edits again or explicitly flushes it. Conflicts never retry.
|
|
97
|
+
*/
|
|
98
|
+
autoSaveRetryDelaysMs?: readonly number[];
|
|
99
|
+
/** Optional synchronous crash-recovery journal owned by this session. */
|
|
100
|
+
recoveryJournal?: DocumentRecoveryJournal | null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Scope captured by an editor instance when it mounts. */
|
|
104
|
+
export interface DocumentSessionEditScope {
|
|
105
|
+
targetKey: string;
|
|
106
|
+
generation: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Result produced while a session transition is frozen. */
|
|
110
|
+
export interface DocumentSessionTransition {
|
|
111
|
+
target: DocumentCommitTarget | null;
|
|
112
|
+
content: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export type DocumentExternalChangeResult = 'applied' | 'conflict' | 'ignored';
|
|
116
|
+
export type DocumentConflictStrategy = 'use-local' | 'use-external';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
type LockNavigator = Navigator & {
|
|
2
|
+
locks?: {
|
|
3
|
+
request<T>(name: string, callback: () => Promise<T>): Promise<T>;
|
|
4
|
+
};
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const localTails = new Map<string, Promise<void>>();
|
|
8
|
+
|
|
9
|
+
/** Serialize conditional commits across providers and, when available, tabs. */
|
|
10
|
+
export async function withFileCommitLock<T>(key: string, operation: () => Promise<T>): Promise<T> {
|
|
11
|
+
const lockManager =
|
|
12
|
+
typeof navigator === 'undefined' ? undefined : (navigator as LockNavigator).locks;
|
|
13
|
+
if (lockManager) {
|
|
14
|
+
return lockManager.request(`docblocks:${key}`, operation);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const previous = localTails.get(key) ?? Promise.resolve();
|
|
18
|
+
const run = previous.then(operation, operation);
|
|
19
|
+
const tail = run.then(
|
|
20
|
+
() => undefined,
|
|
21
|
+
() => undefined,
|
|
22
|
+
);
|
|
23
|
+
localTails.set(key, tail);
|
|
24
|
+
try {
|
|
25
|
+
return await run;
|
|
26
|
+
} finally {
|
|
27
|
+
if (localTails.get(key) === tail) localTails.delete(key);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { ContentContainer } from '@bendyline/squisq/storage';
|
|
2
|
+
import { documentCompanionPath } from './move-entry.js';
|
|
3
|
+
import type {
|
|
4
|
+
MemoryFileSystemProvider,
|
|
5
|
+
MemoryFileSystemSnapshot,
|
|
6
|
+
MemoryFileSystemSnapshotFile,
|
|
7
|
+
} from './memory-provider.js';
|
|
8
|
+
|
|
9
|
+
export type DbkWorkspaceAssetLayout = 'auto' | 'preserve' | 'companion';
|
|
10
|
+
|
|
11
|
+
export interface DbkWorkspaceSnapshotOptions {
|
|
12
|
+
/** Provider-relative path of the transient workspace's active markdown file. */
|
|
13
|
+
targetDocumentPath: string;
|
|
14
|
+
/**
|
|
15
|
+
* `preserve` treats the DBK as a serialized workspace tree. `companion`
|
|
16
|
+
* rebases non-document files beneath `<document>_files/`. `auto` preserves
|
|
17
|
+
* a DBK whose primary path already matches the target, otherwise rebasing
|
|
18
|
+
* imported media into the companion directory.
|
|
19
|
+
*/
|
|
20
|
+
assetLayout?: DbkWorkspaceAssetLayout;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface DbkWorkspaceSnapshot extends MemoryFileSystemSnapshot {
|
|
24
|
+
readonly sourceDocumentPath: string;
|
|
25
|
+
readonly targetDocumentPath: string;
|
|
26
|
+
readonly documentContent: string;
|
|
27
|
+
readonly assetLayout: Exclude<DbkWorkspaceAssetLayout, 'auto'>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function normaliseRelativePath(path: string, label: string): string {
|
|
31
|
+
if (path.includes('\\') || path.includes('\0')) {
|
|
32
|
+
throw new Error(`DBK ${label} must use a safe forward-slash path: ${path}`);
|
|
33
|
+
}
|
|
34
|
+
const normalized = path.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/');
|
|
35
|
+
const segments = normalized ? normalized.split('/') : [];
|
|
36
|
+
if (segments.length === 0 || segments.some((segment) => segment === '.' || segment === '..')) {
|
|
37
|
+
throw new Error(`DBK ${label} must not be empty or traverse directories: ${path}`);
|
|
38
|
+
}
|
|
39
|
+
return segments.join('/');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function joinPath(parent: string, child: string): string {
|
|
43
|
+
return `${parent.replace(/\/+$/g, '')}/${child.replace(/^\/+/, '')}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Fully materialize and validate a DBK container before touching its target
|
|
48
|
+
* provider. A missing or unreadable entry fails the snapshot as a whole.
|
|
49
|
+
*/
|
|
50
|
+
export async function createDbkWorkspaceSnapshot(
|
|
51
|
+
source: ContentContainer,
|
|
52
|
+
options: DbkWorkspaceSnapshotOptions,
|
|
53
|
+
): Promise<DbkWorkspaceSnapshot> {
|
|
54
|
+
const targetDocumentPath = normaliseRelativePath(
|
|
55
|
+
options.targetDocumentPath,
|
|
56
|
+
'target document path',
|
|
57
|
+
);
|
|
58
|
+
const discoveredDocumentPath = await source.getDocumentPath();
|
|
59
|
+
if (!discoveredDocumentPath)
|
|
60
|
+
throw new Error('The external DBK has no primary markdown document.');
|
|
61
|
+
const sourceDocumentPath = normaliseRelativePath(discoveredDocumentPath, 'primary document path');
|
|
62
|
+
const requestedLayout = options.assetLayout ?? 'auto';
|
|
63
|
+
const assetLayout: Exclude<DbkWorkspaceAssetLayout, 'auto'> =
|
|
64
|
+
requestedLayout === 'auto'
|
|
65
|
+
? sourceDocumentPath === targetDocumentPath
|
|
66
|
+
? 'preserve'
|
|
67
|
+
: 'companion'
|
|
68
|
+
: requestedLayout;
|
|
69
|
+
const companionPath = normaliseRelativePath(
|
|
70
|
+
documentCompanionPath(targetDocumentPath),
|
|
71
|
+
'companion directory path',
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const listed = await source.listFiles();
|
|
75
|
+
const entries = [...listed].sort((a, b) => a.path.localeCompare(b.path));
|
|
76
|
+
const seenSourcePaths = new Set<string>();
|
|
77
|
+
const files: MemoryFileSystemSnapshotFile[] = [];
|
|
78
|
+
let documentContent: string | null = null;
|
|
79
|
+
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const sourcePath = normaliseRelativePath(entry.path, 'entry path');
|
|
82
|
+
if (seenSourcePaths.has(sourcePath)) {
|
|
83
|
+
throw new Error(`The external DBK contains duplicate paths: ${sourcePath}`);
|
|
84
|
+
}
|
|
85
|
+
seenSourcePaths.add(sourcePath);
|
|
86
|
+
|
|
87
|
+
const data = await source.readFile(entry.path);
|
|
88
|
+
if (data === null) {
|
|
89
|
+
throw new Error(`The external DBK changed while reading: ${sourcePath}`);
|
|
90
|
+
}
|
|
91
|
+
if (sourcePath === sourceDocumentPath) {
|
|
92
|
+
documentContent = new TextDecoder().decode(data);
|
|
93
|
+
files.push({ kind: 'text', path: targetDocumentPath, content: documentContent });
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const targetPath =
|
|
98
|
+
assetLayout === 'preserve' ? sourcePath : joinPath(companionPath, sourcePath);
|
|
99
|
+
if (/\.(?:md|mdx|txt)$/i.test(sourcePath)) {
|
|
100
|
+
files.push({ kind: 'text', path: targetPath, content: new TextDecoder().decode(data) });
|
|
101
|
+
} else {
|
|
102
|
+
files.push({ kind: 'binary', path: targetPath, data });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (documentContent === null) {
|
|
107
|
+
throw new Error(`The external DBK did not include its primary document: ${sourceDocumentPath}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
sourceDocumentPath,
|
|
112
|
+
targetDocumentPath,
|
|
113
|
+
documentContent,
|
|
114
|
+
assetLayout,
|
|
115
|
+
files,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Replace a transient DBK workspace as one logical operation.
|
|
121
|
+
*
|
|
122
|
+
* Container reads and validation happen before provider mutation. The memory
|
|
123
|
+
* provider then stages and swaps the full tree synchronously, which removes
|
|
124
|
+
* stale files and guarantees the old tree survives any failed replacement.
|
|
125
|
+
*/
|
|
126
|
+
export async function replaceMemoryWorkspaceFromDbk(
|
|
127
|
+
provider: MemoryFileSystemProvider,
|
|
128
|
+
source: ContentContainer,
|
|
129
|
+
options: DbkWorkspaceSnapshotOptions,
|
|
130
|
+
): Promise<DbkWorkspaceSnapshot> {
|
|
131
|
+
const expectedTreeVersion = provider.treeVersion;
|
|
132
|
+
const snapshot = await createDbkWorkspaceSnapshot(source, options);
|
|
133
|
+
provider.replaceContents(snapshot, expectedTreeVersion);
|
|
134
|
+
return snapshot;
|
|
135
|
+
}
|