@lapis-notes/api 0.1.3 → 0.2.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/CHANGELOG.md +304 -13
- package/README.md +19 -0
- package/dist/__tests__/fixtures/indexed-mutations.json +6862 -0
- package/dist/__tests__/fixtures/memory-storage.json +309 -0
- package/dist/__tests__/fixtures/native-facade-contract.json +3224 -0
- package/dist/__tests__/fixtures/projection-query.json +195 -0
- package/dist/__tests__/fixtures/projection-values.json +60 -0
- package/dist/__tests__/fixtures/search-preparation.json +1513 -0
- package/dist/__tests__/fixtures/search-retrieval.json +15409 -0
- package/dist/__tests__/fixtures/search-semantic-model.json +3691 -0
- package/dist/__tests__/fixtures/search-semantic-retrieval.json +6603 -0
- package/dist/__tests__/fixtures/search-storage.json +409 -0
- package/dist/__tests__/fixtures/task-projection.json +428 -0
- package/dist/__tests__/indexed-mutation-support.js +262 -0
- package/dist/__tests__/projection-query-support.js +40 -0
- package/dist/__tests__/search-retrieval-support.js +47 -0
- package/dist/app-database-host.d.ts +1 -0
- package/dist/app-shell-plugin-persistence.d.ts +3 -2
- package/dist/app-shell-plugin-persistence.js +37 -3
- package/dist/cache.svelte.d.ts +8 -1
- package/dist/cache.svelte.js +103 -5
- package/dist/command.svelte.js +29 -2
- package/dist/components/editor/editor.css +4 -0
- package/dist/components/editor/editor.svelte +24 -11
- package/dist/components/editor/embedded-editor-surface.js +3 -1
- package/dist/components/editor/inline-title-alignment.d.ts +3 -0
- package/dist/components/editor/inline-title-alignment.js +57 -0
- package/dist/configuration.svelte.d.ts +270 -206
- package/dist/configuration.svelte.js +161 -86
- package/dist/context.svelte.d.ts +23 -3
- package/dist/context.svelte.js +68 -30
- package/dist/diagnostics.d.ts +8 -0
- package/dist/diagnostics.js +6 -0
- package/dist/editor-save-context.d.ts +35 -0
- package/dist/editor-save-context.js +132 -0
- package/dist/editor-view-registry.d.ts +10 -0
- package/dist/editor-view-registry.js +44 -4
- package/dist/editor.svelte.d.ts +19 -1
- package/dist/editor.svelte.js +528 -55
- package/dist/embed-registry.d.ts +10 -0
- package/dist/embed-registry.js +15 -0
- package/dist/file-editing-failures.d.ts +2 -0
- package/dist/file-editing-failures.js +29 -0
- package/dist/file-editing-native-issues.d.ts +19 -0
- package/dist/file-editing-native-issues.js +65 -0
- package/dist/file-editing-sessions.d.ts +76 -0
- package/dist/file-editing-sessions.js +367 -0
- package/dist/file-manager.d.ts +9 -1
- package/dist/file-manager.js +45 -3
- package/dist/file-state-store.d.ts +5 -0
- package/dist/file-state-store.js +34 -0
- package/dist/file-text-conflicts.d.ts +25 -0
- package/dist/file-text-conflicts.js +35 -0
- package/dist/file-text-mutation.d.ts +13 -0
- package/dist/file-text-mutation.js +100 -0
- package/dist/file-text-persistence.d.ts +36 -0
- package/dist/file-text-persistence.js +234 -0
- package/dist/file-text-state-writer.d.ts +34 -0
- package/dist/file-text-state-writer.js +183 -0
- package/dist/files-configuration.d.ts +20 -0
- package/dist/files-configuration.js +47 -0
- package/dist/generated/plugin-host-modules.schema.generated.d.ts +114 -34
- package/dist/generated/plugin-host-modules.schema.generated.js +132 -34
- package/dist/glob.d.ts +3 -0
- package/dist/glob.js +2 -1
- package/dist/icons/changes.d.ts +3 -0
- package/dist/icons/changes.js +16 -0
- package/dist/icons/index.d.ts +5 -1
- package/dist/icons/index.js +44 -3
- package/dist/icons/registry.d.ts +35 -0
- package/dist/icons/registry.js +154 -0
- package/dist/icons/safe-svg.d.ts +2 -0
- package/dist/icons/safe-svg.js +44 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +4 -1
- package/dist/lapis-extension.d.ts +6 -2
- package/dist/lapis-extension.js +2 -0
- package/dist/metadata-store.d.ts +23 -0
- package/dist/metadata-store.js +1 -0
- package/dist/metadata.svelte.js +45 -2
- package/dist/native-background-indexing.d.ts +48 -0
- package/dist/native-background-indexing.js +208 -0
- package/dist/native-database-protocol.d.ts +29 -0
- package/dist/native-database-protocol.js +1 -0
- package/dist/native-editing-intent-lease.d.ts +17 -0
- package/dist/native-editing-intent-lease.js +188 -0
- package/dist/native-editing-intents-protocol.d.ts +70 -0
- package/dist/native-editing-intents-protocol.js +1 -0
- package/dist/native-editing-intents.d.ts +8 -0
- package/dist/native-editing-intents.js +1 -0
- package/dist/native-editing-problems.d.ts +3 -0
- package/dist/native-editing-problems.js +180 -0
- package/dist/native-editing-recovery.d.ts +24 -0
- package/dist/native-editing-recovery.js +198 -0
- package/dist/native-editing-session.d.ts +51 -0
- package/dist/native-editing-session.js +251 -0
- package/dist/native-history-index-protocol.d.ts +80 -0
- package/dist/native-history-index-protocol.js +1 -0
- package/dist/native-history-index.d.ts +6 -0
- package/dist/native-history-index.js +1 -0
- package/dist/native-indexing-protocol.d.ts +63 -0
- package/dist/native-indexing-protocol.js +1 -0
- package/dist/native-participant-protocol.d.ts +67 -0
- package/dist/native-participant-protocol.js +3 -0
- package/dist/native-queue-protocol.d.ts +73 -0
- package/dist/native-queue-protocol.js +3 -0
- package/dist/native-tasks-protocol.d.ts +264 -0
- package/dist/native-tasks-protocol.js +1 -0
- package/dist/native-tasks.d.ts +35 -0
- package/dist/native-tasks.js +79 -0
- package/dist/native-turn-protocol.d.ts +81 -0
- package/dist/native-turn-protocol.js +1 -0
- package/dist/native-turns.d.ts +29 -0
- package/dist/native-turns.js +2 -0
- package/dist/plugin-asset-server.js +3 -2
- package/dist/plugin-dependency-resolver.d.ts +1 -1
- package/dist/plugin-distribution/compatibility.d.ts +1 -1
- package/dist/plugin-distribution/compatibility.js +10 -5
- package/dist/plugin-distribution/installed-plugin-state.js +2 -1
- package/dist/plugin-distribution/installer.d.ts +6 -0
- package/dist/plugin-distribution/installer.js +90 -37
- package/dist/plugin-distribution/manager.d.ts +4 -1
- package/dist/plugin-distribution/manager.js +12 -5
- package/dist/plugin-distribution/registry-cache.js +2 -1
- package/dist/plugin-distribution/registry-source-state.js +2 -1
- package/dist/plugin-distribution/schemas.d.ts +26 -8
- package/dist/plugin-distribution/schemas.js +2 -1
- package/dist/plugin-distribution/types.d.ts +4 -0
- package/dist/plugin-manager.d.ts +21 -5
- package/dist/plugin-manager.js +228 -64
- package/dist/plugin-runtime-entry.d.ts +1 -1
- package/dist/plugin-runtime-entry.js +16 -0
- package/dist/plugin.d.ts +19 -3
- package/dist/plugin.js +110 -44
- package/dist/registry-schema/catalog-v1.d.ts +60 -9
- package/dist/registry-schema/catalog-v1.js +2 -1
- package/dist/registry-schema/catalog-v2.d.ts +60 -9
- package/dist/registry-schema/catalog-v2.js +1 -1
- package/dist/search-result-provider.d.ts +95 -0
- package/dist/search-result-provider.js +42 -0
- package/dist/status-bar.svelte.d.ts +26 -20
- package/dist/status-bar.svelte.js +1 -0
- package/dist/storage/app-database.d.ts +9 -23
- package/dist/storage/app-database.js +41 -34
- package/dist/storage/desktop-native.d.ts +5 -3
- package/dist/storage/desktop-native.js +59 -6
- package/dist/storage/directory-watcher.d.ts +27 -0
- package/dist/storage/directory-watcher.js +201 -0
- package/dist/storage/fs.d.ts +14 -27
- package/dist/storage/fs.js +2 -168
- package/dist/storage/index-projection.js +33 -15
- package/dist/storage/memory-vault-adapter.d.ts +2 -0
- package/dist/storage/memory-vault-adapter.js +14 -1
- package/dist/storage/path.d.ts +6 -2
- package/dist/storage/path.js +8 -2
- package/dist/storage/projection-query-candidates.d.ts +3 -0
- package/dist/storage/projection-query-candidates.js +69 -0
- package/dist/storage/remote-app-database.d.ts +2 -2
- package/dist/storage/remote-app-database.js +1 -1
- package/dist/storage/runtime.d.ts +3 -0
- package/dist/storage/runtime.js +3 -0
- package/dist/storage/search-embedding-record.d.ts +3 -0
- package/dist/storage/search-embedding-record.js +18 -0
- package/dist/storage/turso-app-database.d.ts +2 -0
- package/dist/storage/turso-app-database.js +67 -30
- package/dist/storage/vault-listing.d.ts +3 -0
- package/dist/storage/vault-listing.js +36 -0
- package/dist/storage/vault-reconciliation.d.ts +19 -0
- package/dist/storage/vault-reconciliation.js +137 -0
- package/dist/storage/vault-session.d.ts +8 -0
- package/dist/storage/vault-session.js +10 -2
- package/dist/storage/vault.svelte.d.ts +53 -4
- package/dist/storage/vault.svelte.js +223 -87
- package/dist/sync-editor-flush.d.ts +6 -0
- package/dist/sync-editor-flush.js +22 -0
- package/dist/sync-host-local.d.ts +15 -0
- package/dist/sync-host-local.js +228 -0
- package/dist/sync-host-security.d.ts +24 -9
- package/dist/sync-host-security.js +47 -14
- package/dist/sync-host.d.ts +33 -4
- package/dist/sync-host.js +160 -10
- package/dist/sync-repository.d.ts +106 -0
- package/dist/sync-repository.js +18 -0
- package/dist/vault-change-views.d.ts +4 -0
- package/dist/vault-change-views.js +43 -0
- package/dist/vault-changes.d.ts +37 -0
- package/dist/vault-changes.js +228 -0
- package/dist/view.svelte.d.ts +182 -149
- package/dist/view.svelte.js +39 -21
- package/dist/workspace-close-guard.d.ts +3 -0
- package/dist/workspace-close-guard.js +35 -0
- package/dist/workspace-host-internal.js +10 -2
- package/dist/workspace-layout-normalizer.d.ts +4 -2
- package/dist/workspace-layout-normalizer.js +5 -5
- package/dist/workspace-view-document.d.ts +3 -0
- package/dist/workspace-view-document.js +12 -0
- package/dist/workspace.svelte.d.ts +13 -2
- package/dist/workspace.svelte.js +371 -82
- package/package.json +61 -8
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { projectVaultTree, scanVaultTree } from "./vault-reconciliation";
|
|
2
|
+
import { DEFAULT_APP_STATE_DIRECTORY } from "./path";
|
|
1
3
|
import { EventDispatcher } from "../events";
|
|
2
4
|
import { hasEditorAssociationGlobMagic, matchesEditorAssociationGlob, normalizeEditorAssociationGlob, validateEditorAssociationGlob, } from "../glob";
|
|
3
5
|
import { FileCache, TFile, TFolder, } from "./fs";
|
|
4
6
|
import { dirname, joinPath, normalizePath } from "./path";
|
|
7
|
+
import { createVaultListingFilter, DEFAULT_VAULT_EXCLUDES, } from "./vault-listing";
|
|
5
8
|
function Err(name) {
|
|
6
9
|
return class extends Error {
|
|
7
10
|
code = "";
|
|
@@ -28,13 +31,37 @@ export const ENOTDIR = Err("ENOTDIR");
|
|
|
28
31
|
*/
|
|
29
32
|
export class Vault extends EventDispatcher {
|
|
30
33
|
adapter;
|
|
34
|
+
operations;
|
|
31
35
|
files = {};
|
|
32
36
|
filesByName = new Map();
|
|
33
|
-
|
|
37
|
+
mutationTail = Promise.resolve();
|
|
38
|
+
mutate(operation) {
|
|
39
|
+
const enqueue = () => {
|
|
40
|
+
const pending = this.mutationTail.then(async () => {
|
|
41
|
+
await this.load();
|
|
42
|
+
return operation();
|
|
43
|
+
});
|
|
44
|
+
this.mutationTail = pending.then(() => { }, () => { });
|
|
45
|
+
return pending;
|
|
46
|
+
};
|
|
47
|
+
return this.operations ? this.operations.track(enqueue) : enqueue();
|
|
48
|
+
}
|
|
49
|
+
mutateFile(file, operation) {
|
|
50
|
+
const path = file.path;
|
|
51
|
+
return this.mutate(async () => {
|
|
52
|
+
if (file.path !== path)
|
|
53
|
+
throw new Error("Mutation target changed during admission");
|
|
54
|
+
return operation();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
configDir = DEFAULT_APP_STATE_DIRECTORY;
|
|
34
58
|
cache = new FileCache(1024);
|
|
35
|
-
|
|
59
|
+
listingFilter;
|
|
60
|
+
constructor(adapter, operations, options = {}) {
|
|
36
61
|
super();
|
|
37
62
|
this.adapter = adapter;
|
|
63
|
+
this.operations = operations;
|
|
64
|
+
this.listingFilter = createVaultListingFilter(options.listingExclusions ?? (() => DEFAULT_VAULT_EXCLUDES));
|
|
38
65
|
this.load();
|
|
39
66
|
}
|
|
40
67
|
getPath(path) {
|
|
@@ -53,7 +80,14 @@ export class Vault extends EventDispatcher {
|
|
|
53
80
|
return this.adapter.stat(this.getPath(path));
|
|
54
81
|
}
|
|
55
82
|
list(path = "/") {
|
|
56
|
-
|
|
83
|
+
const base = this.getPath(path);
|
|
84
|
+
const included = this.listingFilter();
|
|
85
|
+
if (!included(base))
|
|
86
|
+
return Promise.resolve({ files: [], folders: [] });
|
|
87
|
+
return this.adapter.list(base).then(({ files, folders }) => ({
|
|
88
|
+
files: files.filter((entry) => included(joinPath(base, entry))),
|
|
89
|
+
folders: folders.filter((entry) => included(joinPath(base, entry))),
|
|
90
|
+
}));
|
|
57
91
|
}
|
|
58
92
|
#loaded = null;
|
|
59
93
|
load() {
|
|
@@ -67,14 +101,54 @@ export class Vault extends EventDispatcher {
|
|
|
67
101
|
return this.#loaded;
|
|
68
102
|
}
|
|
69
103
|
reload() {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.files = files;
|
|
73
|
-
this.rebuildFileNameIndex();
|
|
104
|
+
return this.mutate(async () => {
|
|
105
|
+
await this.reconcileNow();
|
|
74
106
|
this.trigger("load");
|
|
75
107
|
return this.getRoot();
|
|
76
108
|
});
|
|
77
109
|
}
|
|
110
|
+
/** Reconcile a complete disk snapshot without replacing live resources. */
|
|
111
|
+
reconcile(hints = [], current = () => true, context = {}) {
|
|
112
|
+
return this.mutate(() => this.reconcileNow(hints, current, context));
|
|
113
|
+
}
|
|
114
|
+
async reconcileNow(hints = [], current = () => true, context = {}) {
|
|
115
|
+
await this.load();
|
|
116
|
+
const snapshot = await scanVaultTree(this.adapter, current);
|
|
117
|
+
if (!snapshot || !current())
|
|
118
|
+
return;
|
|
119
|
+
const projected = projectVaultTree(this, this.files, snapshot, hints);
|
|
120
|
+
this.files = projected.files;
|
|
121
|
+
this.rebuildFileNameIndex();
|
|
122
|
+
for (const event of projected.events) {
|
|
123
|
+
if (event.type === "rename")
|
|
124
|
+
this.cache.rename(event.oldPath, event.file.path);
|
|
125
|
+
else
|
|
126
|
+
this.cache.invalidate(event.file.path);
|
|
127
|
+
if (event.type === "rename")
|
|
128
|
+
this.dispatch("rename", event.file, event.oldPath);
|
|
129
|
+
else
|
|
130
|
+
this.dispatch(event.type, event.file);
|
|
131
|
+
this.dispatch("all", event.type, event.file, {
|
|
132
|
+
...context,
|
|
133
|
+
...(event.oldPath === undefined ? {} : { oldPath: event.oldPath }),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
recordFile(path, stat) {
|
|
138
|
+
const existing = this.files[path];
|
|
139
|
+
if (existing instanceof TFile) {
|
|
140
|
+
Object.assign(existing.stat, stat);
|
|
141
|
+
return existing;
|
|
142
|
+
}
|
|
143
|
+
const parent = this.files[dirname(path)];
|
|
144
|
+
if (!(parent instanceof TFolder))
|
|
145
|
+
throw new ENOTDIR(dirname(path));
|
|
146
|
+
const file = new TFile(path, stat, parent, this);
|
|
147
|
+
this.files[path] = file;
|
|
148
|
+
this.addToFileNameIndex(file);
|
|
149
|
+
parent.children.push(file);
|
|
150
|
+
return file;
|
|
151
|
+
}
|
|
78
152
|
addToFileNameIndex(file) {
|
|
79
153
|
const paths = this.filesByName.get(file.name) ?? new Set();
|
|
80
154
|
paths.add(file.path);
|
|
@@ -136,10 +210,14 @@ export class Vault extends EventDispatcher {
|
|
|
136
210
|
* @public
|
|
137
211
|
*/
|
|
138
212
|
create(path, data, options) {
|
|
213
|
+
const run = () => this.createNow(path, data, options);
|
|
214
|
+
return this.mutate(run);
|
|
215
|
+
}
|
|
216
|
+
createNow(path, data, options) {
|
|
139
217
|
path = this.getPath(path);
|
|
140
218
|
const file = this.getFileByPath(path);
|
|
141
219
|
if (file) {
|
|
142
|
-
return this.
|
|
220
|
+
return this.modifyNow(file, data, options).then(() => file);
|
|
143
221
|
}
|
|
144
222
|
return this.adapter
|
|
145
223
|
.write(path, data, options)
|
|
@@ -148,16 +226,51 @@ export class Vault extends EventDispatcher {
|
|
|
148
226
|
if (!stat) {
|
|
149
227
|
return Promise.reject(`Unable to write file: ${path}`);
|
|
150
228
|
}
|
|
151
|
-
|
|
229
|
+
const file = this.recordFile(path, stat);
|
|
230
|
+
this.cache.put(path, data);
|
|
231
|
+
this.dispatch("create", file);
|
|
232
|
+
this.dispatch("all", "create", file, {});
|
|
233
|
+
return file;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
get supportsExclusiveCreate() {
|
|
237
|
+
const adapter = this.adapter;
|
|
238
|
+
return (adapter.getCapabilities?.().exclusiveCreate === true &&
|
|
239
|
+
typeof adapter.writeExclusive === "function");
|
|
240
|
+
}
|
|
241
|
+
/** Create new bytes without replacing a cached or newly arriving file. */
|
|
242
|
+
createExclusive(path, data, options) {
|
|
243
|
+
if (!this.supportsExclusiveCreate)
|
|
244
|
+
return Promise.reject(Object.assign(new Error("Exclusive file creation is unavailable"), {
|
|
245
|
+
code: "ENOTSUP",
|
|
246
|
+
}));
|
|
247
|
+
const run = async () => {
|
|
248
|
+
await this.load();
|
|
249
|
+
path = this.getPath(path);
|
|
250
|
+
if (this.files[path])
|
|
251
|
+
throw Object.assign(new Error(`A file already exists at ${path}`), {
|
|
252
|
+
code: "EEXIST",
|
|
253
|
+
});
|
|
254
|
+
const parent = this.files[dirname(path)];
|
|
255
|
+
if (!(parent instanceof TFolder))
|
|
256
|
+
throw new ENOTDIR(dirname(path));
|
|
257
|
+
await this.adapter.writeExclusive(path, data, options);
|
|
258
|
+
const stat = await this.adapter.stat(path);
|
|
259
|
+
if (!stat || stat.type !== "file")
|
|
260
|
+
throw new Error(`Unable to create file: ${path}`);
|
|
152
261
|
const file = new TFile(path, stat, parent, this);
|
|
153
262
|
this.files[path] = file;
|
|
154
263
|
this.addToFileNameIndex(file);
|
|
155
|
-
|
|
264
|
+
if (typeof data === "string")
|
|
265
|
+
this.cache.put(path, data);
|
|
266
|
+
else
|
|
267
|
+
this.cache.invalidate(path);
|
|
156
268
|
parent.children.push(file);
|
|
157
269
|
this.dispatch("create", file);
|
|
158
270
|
this.dispatch("all", "create", file, {});
|
|
159
271
|
return file;
|
|
160
|
-
}
|
|
272
|
+
};
|
|
273
|
+
return this.mutate(run);
|
|
161
274
|
}
|
|
162
275
|
/**
|
|
163
276
|
* Create a new binary file inside the vault.
|
|
@@ -169,6 +282,10 @@ export class Vault extends EventDispatcher {
|
|
|
169
282
|
* @public
|
|
170
283
|
*/
|
|
171
284
|
createBinary(path, data, options) {
|
|
285
|
+
const run = () => this.createBinaryNow(path, data, options);
|
|
286
|
+
return this.mutate(run);
|
|
287
|
+
}
|
|
288
|
+
createBinaryNow(path, data, options) {
|
|
172
289
|
path = this.getPath(path);
|
|
173
290
|
return this.adapter
|
|
174
291
|
.writeBinary(path, data, options)
|
|
@@ -177,11 +294,8 @@ export class Vault extends EventDispatcher {
|
|
|
177
294
|
if (!stat) {
|
|
178
295
|
return Promise.reject(`Unable to write file: ${path}`);
|
|
179
296
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
this.files[path] = file;
|
|
183
|
-
this.addToFileNameIndex(file);
|
|
184
|
-
parent.children.push(file);
|
|
297
|
+
const file = this.recordFile(path, stat);
|
|
298
|
+
this.cache.invalidate(path);
|
|
185
299
|
this.dispatch("create", file);
|
|
186
300
|
this.dispatch("all", "create", file, {});
|
|
187
301
|
return file;
|
|
@@ -194,7 +308,11 @@ export class Vault extends EventDispatcher {
|
|
|
194
308
|
* @throws Error if folder already exists
|
|
195
309
|
* @public
|
|
196
310
|
*/
|
|
197
|
-
|
|
311
|
+
createFolder(path) {
|
|
312
|
+
const run = () => this.createFolderNow(path);
|
|
313
|
+
return this.mutate(run);
|
|
314
|
+
}
|
|
315
|
+
async createFolderNow(path) {
|
|
198
316
|
path = this.getPath(path);
|
|
199
317
|
try {
|
|
200
318
|
const folder = await this.adapter.mkdir(path).then(() => {
|
|
@@ -261,6 +379,9 @@ export class Vault extends EventDispatcher {
|
|
|
261
379
|
* @public
|
|
262
380
|
*/
|
|
263
381
|
delete(file, force, context = {}) {
|
|
382
|
+
return this.mutateFile(file, () => this.deleteNow(file, force, context));
|
|
383
|
+
}
|
|
384
|
+
deleteNow(file, force, context = {}) {
|
|
264
385
|
const remove = file instanceof TFolder
|
|
265
386
|
? this.adapter.rmdir(file.path, force ?? true)
|
|
266
387
|
: this.adapter.remove(file.path);
|
|
@@ -286,18 +407,16 @@ export class Vault extends EventDispatcher {
|
|
|
286
407
|
});
|
|
287
408
|
}
|
|
288
409
|
trash(file, system = true) {
|
|
410
|
+
return this.mutateFile(file, () => this.trashNow(file, system));
|
|
411
|
+
}
|
|
412
|
+
trashNow(file, system = true) {
|
|
289
413
|
const trash = system
|
|
290
414
|
? this.adapter.trashSystem(file.path)
|
|
291
415
|
: Promise.resolve(false);
|
|
292
|
-
return trash.then((handled) => {
|
|
293
|
-
if (handled)
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
return this.adapter.trashLocal(file.path).then(async () => {
|
|
297
|
-
await this.reload();
|
|
298
|
-
this.dispatch("delete", file);
|
|
299
|
-
this.dispatch("all", "delete", file, { system });
|
|
300
|
-
});
|
|
416
|
+
return trash.then(async (handled) => {
|
|
417
|
+
if (!handled)
|
|
418
|
+
await this.adapter.trashLocal(file.path);
|
|
419
|
+
await this.reconcileNow([], () => true, { system });
|
|
301
420
|
});
|
|
302
421
|
}
|
|
303
422
|
/**
|
|
@@ -360,7 +479,10 @@ export class Vault extends EventDispatcher {
|
|
|
360
479
|
};
|
|
361
480
|
}
|
|
362
481
|
/** Stream a file into the adapter's atomic or strongest safe replacement path. */
|
|
363
|
-
|
|
482
|
+
atomicWrite(path, source, options = {}) {
|
|
483
|
+
return this.mutate(() => this.atomicWriteNow(path, source, options));
|
|
484
|
+
}
|
|
485
|
+
async atomicWriteNow(path, source, options = {}) {
|
|
364
486
|
path = this.getPath(path);
|
|
365
487
|
const existed = this.getFileByPath(path);
|
|
366
488
|
const adapter = this.adapter;
|
|
@@ -374,7 +496,7 @@ export class Vault extends EventDispatcher {
|
|
|
374
496
|
try {
|
|
375
497
|
while (true) {
|
|
376
498
|
if (options.signal?.aborted) {
|
|
377
|
-
throw options.signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
499
|
+
throw (options.signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
378
500
|
}
|
|
379
501
|
const next = await reader.read();
|
|
380
502
|
if (next.done)
|
|
@@ -394,17 +516,10 @@ export class Vault extends EventDispatcher {
|
|
|
394
516
|
}
|
|
395
517
|
await adapter.writeBinary(path, data.buffer, options);
|
|
396
518
|
}
|
|
397
|
-
this.
|
|
398
|
-
this.rebuildFileNameIndex();
|
|
519
|
+
await this.reconcileNow([{ type: existed ? "modify" : "create", path }], () => true, { operationOrigin: options.operationOrigin });
|
|
399
520
|
const file = this.getFileByPath(path);
|
|
400
521
|
if (!file)
|
|
401
522
|
throw new Error(`Unable to write file: ${path}`);
|
|
402
|
-
this.cache.invalidate(path);
|
|
403
|
-
const event = existed ? "modify" : "create";
|
|
404
|
-
this.dispatch(event, file);
|
|
405
|
-
this.dispatch("all", event, file, {
|
|
406
|
-
operationOrigin: options.operationOrigin,
|
|
407
|
-
});
|
|
408
523
|
return file;
|
|
409
524
|
}
|
|
410
525
|
/**
|
|
@@ -416,33 +531,19 @@ export class Vault extends EventDispatcher {
|
|
|
416
531
|
* @public
|
|
417
532
|
*/
|
|
418
533
|
rename(file, newPath) {
|
|
534
|
+
return this.mutateFile(file, () => this.renameNow(file, newPath));
|
|
535
|
+
}
|
|
536
|
+
renameNow(file, newPath) {
|
|
419
537
|
newPath = this.getPath(newPath);
|
|
420
538
|
if (file.path == newPath) {
|
|
421
539
|
return Promise.resolve();
|
|
422
540
|
}
|
|
423
541
|
const oldPath = file.path;
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
else {
|
|
430
|
-
paths.add(file.path);
|
|
431
|
-
paths.add(newPath);
|
|
432
|
-
}
|
|
433
|
-
return this.adapter.rename(file.path, newPath).then(async () => {
|
|
434
|
-
this.files = await this.loadPath("/", {});
|
|
435
|
-
this.rebuildFileNameIndex();
|
|
436
|
-
let newFile = this.getAbstractFileByPath(newPath);
|
|
437
|
-
if (newFile) {
|
|
438
|
-
this.cache.rename(file.path, newFile.path);
|
|
439
|
-
this.dispatch("rename", newFile, oldPath);
|
|
440
|
-
this.dispatch("all", "rename", newFile, { oldPath });
|
|
441
|
-
}
|
|
442
|
-
else {
|
|
443
|
-
console.warn(`Failed to rename file: ${file.path}`);
|
|
444
|
-
}
|
|
445
|
-
});
|
|
542
|
+
return this.adapter
|
|
543
|
+
.rename(oldPath, newPath)
|
|
544
|
+
.then(() => this.reconcileNow([
|
|
545
|
+
{ type: "create", path: newPath, previousPath: oldPath },
|
|
546
|
+
]));
|
|
446
547
|
}
|
|
447
548
|
/**
|
|
448
549
|
* Modify the contents of a plaintext file.
|
|
@@ -453,21 +554,41 @@ export class Vault extends EventDispatcher {
|
|
|
453
554
|
* @public
|
|
454
555
|
*/
|
|
455
556
|
modify(file, data, options) {
|
|
557
|
+
return this.mutateFile(file, () => this.modifyNow(file, data, options));
|
|
558
|
+
}
|
|
559
|
+
modifyNow(file, data, options) {
|
|
456
560
|
if (!this.getFileByPath(file.path)) {
|
|
457
|
-
return this.
|
|
561
|
+
return this.createNow(file.path, data, options).then(() => { });
|
|
458
562
|
}
|
|
459
563
|
const path = file.path;
|
|
460
564
|
return this.adapter
|
|
461
565
|
.write(path, data, options)
|
|
462
|
-
.then(() => this
|
|
463
|
-
|
|
566
|
+
.then(() => this.#recordTextWrite(path, data, options));
|
|
567
|
+
}
|
|
568
|
+
get supportsConditionalTextWrites() {
|
|
569
|
+
const adapter = this.adapter;
|
|
570
|
+
return (adapter.getCapabilities?.().conditionalTextWrite === true &&
|
|
571
|
+
typeof adapter.writeTextIfUnchanged === "function");
|
|
572
|
+
}
|
|
573
|
+
/** Persist only the caller's captured text version; never fall back to write. */
|
|
574
|
+
modifyIfUnchanged(file, data, expected, options) {
|
|
575
|
+
return this.mutateFile(file, () => this.modifyIfUnchangedNow(file, data, expected, options));
|
|
576
|
+
}
|
|
577
|
+
async modifyIfUnchangedNow(file, data, expected, options) {
|
|
578
|
+
if (!this.supportsConditionalTextWrites)
|
|
579
|
+
throw new Error("Conditional text writes are unavailable");
|
|
580
|
+
if (expected !== null && typeof expected !== "string")
|
|
581
|
+
throw new Error("Invalid expected text snapshot");
|
|
582
|
+
const path = file.path;
|
|
583
|
+
await this.adapter.writeTextIfUnchanged(path, data, expected, options);
|
|
584
|
+
await this.#recordTextWrite(path, data, options);
|
|
585
|
+
}
|
|
586
|
+
#recordTextWrite(path, data, options) {
|
|
587
|
+
return this.adapter.stat(path).then((stat) => {
|
|
464
588
|
if (!stat) {
|
|
465
589
|
return Promise.reject(`Unable to write file: ${path}`);
|
|
466
590
|
}
|
|
467
|
-
|
|
468
|
-
const file = new TFile(path, stat, parent, this);
|
|
469
|
-
this.files[path] = file;
|
|
470
|
-
this.cache.put(path, data);
|
|
591
|
+
const file = this.recordFile(path, stat);
|
|
471
592
|
this.cache.put(file.path, data);
|
|
472
593
|
this.dispatch("modify", file);
|
|
473
594
|
this.dispatch("all", "modify", file, { data, options });
|
|
@@ -482,8 +603,11 @@ export class Vault extends EventDispatcher {
|
|
|
482
603
|
* @public
|
|
483
604
|
*/
|
|
484
605
|
modifyBinary(file, data, options) {
|
|
606
|
+
return this.mutateFile(file, () => this.modifyBinaryNow(file, data, options));
|
|
607
|
+
}
|
|
608
|
+
modifyBinaryNow(file, data, options) {
|
|
485
609
|
if (!this.getFileByPath(file.path)) {
|
|
486
|
-
return this.
|
|
610
|
+
return this.createBinaryNow(file.path, data, options).then(() => { });
|
|
487
611
|
}
|
|
488
612
|
const path = file.path;
|
|
489
613
|
return this.adapter
|
|
@@ -493,9 +617,8 @@ export class Vault extends EventDispatcher {
|
|
|
493
617
|
if (!stat) {
|
|
494
618
|
return Promise.reject(`Unable to write file: ${path}`);
|
|
495
619
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
this.files[path] = file;
|
|
620
|
+
const file = this.recordFile(path, stat);
|
|
621
|
+
this.cache.invalidate(path);
|
|
499
622
|
this.dispatch("modify", file);
|
|
500
623
|
this.dispatch("all", "modify", file, { data, options });
|
|
501
624
|
});
|
|
@@ -518,14 +641,18 @@ export class Vault extends EventDispatcher {
|
|
|
518
641
|
* @public
|
|
519
642
|
*/
|
|
520
643
|
process(file, fn, options) {
|
|
521
|
-
return this.
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
644
|
+
return this.mutateFile(file, () => this.processNow(file, fn, options));
|
|
645
|
+
}
|
|
646
|
+
processNow(file, fn, options) {
|
|
647
|
+
return this.adapter.process(file.path, fn, options).then(async (data) => {
|
|
648
|
+
await this.#recordTextWrite(file.path, data, options);
|
|
525
649
|
return data;
|
|
526
650
|
});
|
|
527
651
|
}
|
|
528
652
|
append(file, data, options) {
|
|
653
|
+
return this.mutateFile(file, () => this.appendNow(file, data, options));
|
|
654
|
+
}
|
|
655
|
+
appendNow(file, data, options) {
|
|
529
656
|
return this.adapter
|
|
530
657
|
.append(file.path, data, options)
|
|
531
658
|
.then(() => this.adapter.stat(file.path))
|
|
@@ -533,15 +660,16 @@ export class Vault extends EventDispatcher {
|
|
|
533
660
|
if (!stat) {
|
|
534
661
|
return Promise.reject(`Unable to append file: ${file.path}`);
|
|
535
662
|
}
|
|
536
|
-
const
|
|
537
|
-
const next = new TFile(file.path, stat, parent, this);
|
|
538
|
-
this.files[file.path] = next;
|
|
663
|
+
const next = this.recordFile(file.path, stat);
|
|
539
664
|
this.cache.invalidate(file.path);
|
|
540
665
|
this.dispatch("modify", next);
|
|
541
666
|
this.dispatch("all", "modify", next, { data, options });
|
|
542
667
|
});
|
|
543
668
|
}
|
|
544
669
|
appendBinary(file, data, options) {
|
|
670
|
+
return this.mutateFile(file, () => this.appendBinaryNow(file, data, options));
|
|
671
|
+
}
|
|
672
|
+
appendBinaryNow(file, data, options) {
|
|
545
673
|
return this.adapter
|
|
546
674
|
.appendBinary(file.path, data, options)
|
|
547
675
|
.then(() => this.adapter.stat(file.path))
|
|
@@ -549,24 +677,23 @@ export class Vault extends EventDispatcher {
|
|
|
549
677
|
if (!stat) {
|
|
550
678
|
return Promise.reject(`Unable to append file: ${file.path}`);
|
|
551
679
|
}
|
|
552
|
-
const
|
|
553
|
-
const next = new TFile(file.path, stat, parent, this);
|
|
554
|
-
this.files[file.path] = next;
|
|
680
|
+
const next = this.recordFile(file.path, stat);
|
|
555
681
|
this.cache.invalidate(file.path);
|
|
556
682
|
this.dispatch("modify", next);
|
|
557
683
|
this.dispatch("all", "modify", next, { data, options });
|
|
558
684
|
});
|
|
559
685
|
}
|
|
560
|
-
|
|
686
|
+
copy(file, newPath) {
|
|
687
|
+
return this.mutateFile(file, () => this.copyNow(file, newPath));
|
|
688
|
+
}
|
|
689
|
+
async copyNow(file, newPath) {
|
|
561
690
|
newPath = this.getPath(newPath);
|
|
562
691
|
await this.adapter.copy(file.path, newPath);
|
|
563
|
-
await this.
|
|
692
|
+
await this.reconcileNow([], () => true, { originalPath: file.path });
|
|
564
693
|
const copied = this.getAbstractFileByPath(newPath);
|
|
565
694
|
if (!copied) {
|
|
566
695
|
throw new Error(`Unable to copy file: ${file.path}`);
|
|
567
696
|
}
|
|
568
|
-
this.dispatch("create", copied);
|
|
569
|
-
this.dispatch("all", "create", copied, { originalPath: file.path });
|
|
570
697
|
return copied;
|
|
571
698
|
}
|
|
572
699
|
/**
|
|
@@ -636,7 +763,8 @@ export class Vault extends EventDispatcher {
|
|
|
636
763
|
adapter.revokeResourceUrl?.(url);
|
|
637
764
|
}
|
|
638
765
|
getAllLoadedFiles() {
|
|
639
|
-
|
|
766
|
+
const included = this.listingFilter();
|
|
767
|
+
return Object.values(this.files).filter((file) => included(file.path));
|
|
640
768
|
}
|
|
641
769
|
/**
|
|
642
770
|
* Get all folders in the vault.
|
|
@@ -645,7 +773,10 @@ export class Vault extends EventDispatcher {
|
|
|
645
773
|
* @public
|
|
646
774
|
*/
|
|
647
775
|
getAllFolders(includeRoot) {
|
|
648
|
-
|
|
776
|
+
const included = this.listingFilter();
|
|
777
|
+
return Object.values(this.files).filter((it) => it instanceof TFolder &&
|
|
778
|
+
included(it.path) &&
|
|
779
|
+
(includeRoot || it.path !== "/"));
|
|
649
780
|
}
|
|
650
781
|
/**
|
|
651
782
|
* Get all files in the vault.
|
|
@@ -653,7 +784,8 @@ export class Vault extends EventDispatcher {
|
|
|
653
784
|
* @public
|
|
654
785
|
*/
|
|
655
786
|
getFiles() {
|
|
656
|
-
|
|
787
|
+
const included = this.listingFilter();
|
|
788
|
+
return Object.values(this.files).filter((it) => it instanceof TFile && included(it.path));
|
|
657
789
|
}
|
|
658
790
|
/**
|
|
659
791
|
* Iterate loaded files without allocating a second vault-sized collection.
|
|
@@ -666,8 +798,11 @@ export class Vault extends EventDispatcher {
|
|
|
666
798
|
const root = this.files["/"];
|
|
667
799
|
if (!(root instanceof TFolder))
|
|
668
800
|
return;
|
|
801
|
+
const included = this.listingFilter();
|
|
669
802
|
function* visit(folder) {
|
|
670
803
|
for (const child of folder.children) {
|
|
804
|
+
if (!included(child.path))
|
|
805
|
+
continue;
|
|
671
806
|
if (child instanceof TFile)
|
|
672
807
|
yield child.copy();
|
|
673
808
|
else if (child instanceof TFolder)
|
|
@@ -715,9 +850,10 @@ export class Vault extends EventDispatcher {
|
|
|
715
850
|
}
|
|
716
851
|
}
|
|
717
852
|
}
|
|
853
|
+
const included = this.listingFilter();
|
|
718
854
|
return [...paths]
|
|
719
855
|
.map((path) => this.getFileByPath(path))
|
|
720
|
-
.filter((file) => Boolean(file))
|
|
856
|
+
.filter((file) => Boolean(file) && included(file.path))
|
|
721
857
|
.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
722
858
|
}
|
|
723
859
|
getMarkdownFiles() {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Workspace } from "./workspace.svelte";
|
|
2
|
+
/** Capture every workspace-owned editor, including inactive, docked and floating panes. */
|
|
3
|
+
export declare function flushWorkspaceEditors(workspace: Pick<Workspace, "iterateAllLeaves">, path?: string): Promise<{
|
|
4
|
+
flushed: boolean;
|
|
5
|
+
path?: string;
|
|
6
|
+
}>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Capture every workspace-owned editor, including inactive, docked and floating panes. */
|
|
2
|
+
export async function flushWorkspaceEditors(workspace, path) {
|
|
3
|
+
const editors = new Map();
|
|
4
|
+
workspace.iterateAllLeaves((leaf) => {
|
|
5
|
+
const view = leaf.view;
|
|
6
|
+
if (view.editor && view.file && (!path || view.file.path === path))
|
|
7
|
+
editors.set(view.editor, view.file.path);
|
|
8
|
+
});
|
|
9
|
+
const results = await Promise.allSettled([...editors].map(async ([editor, capturedPath]) => {
|
|
10
|
+
await editor.flushChanges();
|
|
11
|
+
if (editor.file?.path !== capturedPath)
|
|
12
|
+
throw new Error("Editor document changed while flushing for Sync");
|
|
13
|
+
}));
|
|
14
|
+
for (const result of results)
|
|
15
|
+
if (result.status === "rejected")
|
|
16
|
+
throw result.reason;
|
|
17
|
+
const only = editors.size === 1 ? editors.values().next().value : undefined;
|
|
18
|
+
return {
|
|
19
|
+
flushed: editors.size > 0,
|
|
20
|
+
...((path ?? only) ? { path: path ?? only } : {}),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HostedPluginCapability } from "./plugin-capability-facade";
|
|
2
|
+
import type { SyncHostLocalProvider, SyncLocalApplyGuard, SyncLocalHostCapabilities } from "./sync-repository";
|
|
3
|
+
/** Grant-checked lifetime boundary; providers retain all platform-specific authority. */
|
|
4
|
+
export declare class ScopedSyncLocalRuntime {
|
|
5
|
+
#private;
|
|
6
|
+
private readonly options;
|
|
7
|
+
constructor(options: {
|
|
8
|
+
runtimeId: string;
|
|
9
|
+
selectProvider(): SyncHostLocalProvider | undefined;
|
|
10
|
+
assertGranted(capability: HostedPluginCapability): void;
|
|
11
|
+
guardApply?: SyncLocalApplyGuard;
|
|
12
|
+
});
|
|
13
|
+
readonly capabilities: SyncLocalHostCapabilities;
|
|
14
|
+
revoke(): Promise<void>;
|
|
15
|
+
}
|