@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,6 +1,8 @@
|
|
|
1
|
+
import { rebindSearchDocument } from "./search-embedding-record";
|
|
1
2
|
import { collectSearchQueryTerms } from "../search-query";
|
|
2
3
|
import { buildSearchResult, compareSearchResults, embeddingErrorState, EMPTY_APP_DATABASE_CAPABILITIES, hasSearchPropertyNames, MIN_VECTOR_SEARCH_SCORE, normalizeSearchDocument, pathWithinPrefix, rankSearchScores, resolveSearchQueryEnhancementDiagnostics, scoreSearchDocument, scoreVectorDocument, searchDocumentProperties, searchPropertyNames, } from "./app-database";
|
|
3
4
|
import { assertProjectionReadAccess, assertProjectionWriteAccess, compileProjectionQuerySql, evaluateProjectionQuery, indexedValuesForRow, MAX_PROJECTION_ROWS_PER_SOURCE, projectionIndexStatus, PUBLIC_TASKS_PROJECTION_ID, } from "./index-projection";
|
|
5
|
+
import { projectionQueryCandidates } from "./projection-query-candidates";
|
|
4
6
|
import { TASK_PROJECTION_FIELDS, TASK_PROJECTION_VERSION, taskQueryToIndexQuery, } from "./task-projection";
|
|
5
7
|
import { createSearchEmbeddingProvider, } from "./search-embedding-provider";
|
|
6
8
|
import { isStructuredSearchQuery } from "./search-query-evaluator";
|
|
@@ -580,6 +582,7 @@ export class TursoAppDatabase {
|
|
|
580
582
|
kind;
|
|
581
583
|
connection = null;
|
|
582
584
|
opened = false;
|
|
585
|
+
closeTask = null;
|
|
583
586
|
nativeFullTextSearch = false;
|
|
584
587
|
nativeVectorSearch = false;
|
|
585
588
|
searchEmbeddingProviderConfig = null;
|
|
@@ -669,33 +672,65 @@ export class TursoAppDatabase {
|
|
|
669
672
|
}
|
|
670
673
|
}
|
|
671
674
|
async close() {
|
|
672
|
-
if (
|
|
673
|
-
return;
|
|
675
|
+
if (this.closeTask)
|
|
676
|
+
return this.closeTask;
|
|
674
677
|
const connection = this.connection;
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
678
|
+
if (!connection)
|
|
679
|
+
return;
|
|
680
|
+
this.closeTask = (async () => {
|
|
681
|
+
await this.commitQueue;
|
|
682
|
+
await this.searchEmbeddingProvider?.dispose?.();
|
|
683
|
+
this.searchEmbeddingProvider = null;
|
|
684
|
+
await connection.close();
|
|
685
|
+
this.connection = null;
|
|
686
|
+
this.opened = false;
|
|
687
|
+
this.changeListeners.clear();
|
|
688
|
+
})();
|
|
689
|
+
try {
|
|
690
|
+
await this.closeTask;
|
|
691
|
+
}
|
|
692
|
+
finally {
|
|
693
|
+
this.closeTask = null;
|
|
694
|
+
}
|
|
681
695
|
}
|
|
682
696
|
async beginSearchIndexingBatch() { }
|
|
683
697
|
async endSearchIndexingBatch() { }
|
|
684
698
|
async configureSearchEmbeddingProvider(provider) {
|
|
699
|
+
const pending = this.commitQueue.then(() => this.configureSearchProviderImmediately(provider));
|
|
700
|
+
this.commitQueue = pending.then(() => undefined, () => undefined);
|
|
701
|
+
return pending;
|
|
702
|
+
}
|
|
703
|
+
async configureSearchProviderImmediately(provider) {
|
|
685
704
|
if (JSON.stringify(provider) ===
|
|
686
705
|
JSON.stringify(this.searchEmbeddingProviderConfig))
|
|
687
706
|
return;
|
|
688
707
|
const previous = this.searchEmbeddingProvider;
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
sql: `INSERT INTO app_meta (key, value_json) VALUES (?, ?)
|
|
708
|
+
const next = createSearchEmbeddingProvider(provider);
|
|
709
|
+
try {
|
|
710
|
+
const statements = [
|
|
711
|
+
{
|
|
712
|
+
sql: `INSERT INTO app_meta (key, value_json) VALUES (?, ?)
|
|
695
713
|
ON CONFLICT(key) DO UPDATE SET value_json = excluded.value_json`,
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
714
|
+
args: [SEARCH_EMBEDDING_PROVIDER_META_KEY, JSON.stringify(provider)],
|
|
715
|
+
},
|
|
716
|
+
];
|
|
717
|
+
if (next)
|
|
718
|
+
for (const document of await this.listSearchDocuments()) {
|
|
719
|
+
statements.push(...this.searchDocumentStatements(rebindSearchDocument(document, next.config)));
|
|
720
|
+
}
|
|
721
|
+
await this.commitImmediately(statements, ["search"], [], undefined, () => {
|
|
722
|
+
this.searchEmbeddingProviderConfig = clone(provider);
|
|
723
|
+
this.searchEmbeddingProvider = next;
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
if (this.searchEmbeddingProvider === next)
|
|
728
|
+
await previous?.dispose?.();
|
|
729
|
+
else
|
|
730
|
+
await next?.dispose?.();
|
|
731
|
+
throw error;
|
|
732
|
+
}
|
|
733
|
+
await previous?.dispose?.();
|
|
699
734
|
}
|
|
700
735
|
async getSearchEmbeddingProvider() {
|
|
701
736
|
return clone(this.searchEmbeddingProviderConfig);
|
|
@@ -1207,7 +1242,7 @@ export class TursoAppDatabase {
|
|
|
1207
1242
|
...input.patch,
|
|
1208
1243
|
ownerId: input.patch.status === "running" ? existing.ownerId : undefined,
|
|
1209
1244
|
leaseUntil: input.patch.status === "running"
|
|
1210
|
-
? input.patch.leaseUntil ?? existing.leaseUntil
|
|
1245
|
+
? (input.patch.leaseUntil ?? existing.leaseUntil)
|
|
1211
1246
|
: undefined,
|
|
1212
1247
|
};
|
|
1213
1248
|
await this.commit([
|
|
@@ -1561,7 +1596,7 @@ export class TursoAppDatabase {
|
|
|
1561
1596
|
Boolean(options.sourceProviderIds?.length) ||
|
|
1562
1597
|
Boolean(options.pathPrefix);
|
|
1563
1598
|
if (allPathsRequired) {
|
|
1564
|
-
return this.evaluateSearchDocumentsForPaths(query, options, await this.queryAllSearchPaths(options
|
|
1599
|
+
return this.evaluateSearchDocumentsForPaths(query, options, await this.queryAllSearchPaths(options));
|
|
1565
1600
|
}
|
|
1566
1601
|
const candidates = new Set();
|
|
1567
1602
|
const includesLexical = requestedMode !== "vector";
|
|
@@ -1588,7 +1623,7 @@ export class TursoAppDatabase {
|
|
|
1588
1623
|
async searchDocumentPaths(query, options = {}) {
|
|
1589
1624
|
return (await this.searchDocuments(query, options)).map((result) => result.document.path);
|
|
1590
1625
|
}
|
|
1591
|
-
async queryAllSearchPaths(
|
|
1626
|
+
async queryAllSearchPaths(options = {}) {
|
|
1592
1627
|
const clauses = [];
|
|
1593
1628
|
const args = [];
|
|
1594
1629
|
if (options.sourceProviderIds?.length) {
|
|
@@ -1601,13 +1636,13 @@ export class TursoAppDatabase {
|
|
|
1601
1636
|
}
|
|
1602
1637
|
const rows = await this.requireConnection().all(`SELECT path FROM search_docs
|
|
1603
1638
|
${clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : ""}
|
|
1604
|
-
ORDER BY path
|
|
1639
|
+
ORDER BY path`, ...args);
|
|
1605
1640
|
return rows.map((row) => row.path);
|
|
1606
1641
|
}
|
|
1607
1642
|
async queryLexicalPaths(query, limit = 100) {
|
|
1608
1643
|
const terms = collectSearchQueryTerms(query);
|
|
1609
1644
|
if (!terms.length || !this.nativeFullTextSearch) {
|
|
1610
|
-
return this.queryAllSearchPaths(
|
|
1645
|
+
return this.queryAllSearchPaths();
|
|
1611
1646
|
}
|
|
1612
1647
|
const ftsQuery = ftsQueryForTerms(terms);
|
|
1613
1648
|
const rows = await this.requireConnection().all(`SELECT path,
|
|
@@ -1857,11 +1892,7 @@ export class TursoAppDatabase {
|
|
|
1857
1892
|
async queryProjection(projectionId, query = {}, readerPluginId) {
|
|
1858
1893
|
const definition = await this.getProjectionDefinition(projectionId);
|
|
1859
1894
|
assertProjectionReadAccess(definition, readerPluginId);
|
|
1860
|
-
const compiled = compileProjectionQuerySql(projectionId,
|
|
1861
|
-
...query,
|
|
1862
|
-
after: undefined,
|
|
1863
|
-
limit: query.after ? undefined : query.limit,
|
|
1864
|
-
}, definition);
|
|
1895
|
+
const compiled = compileProjectionQuerySql(projectionId, projectionQueryCandidates(query), definition);
|
|
1865
1896
|
const [rows, sources] = await Promise.all([
|
|
1866
1897
|
this.requireConnection().all(compiled.sql, ...compiled.args),
|
|
1867
1898
|
this.getProjectionSources(projectionId),
|
|
@@ -1893,8 +1924,13 @@ export class TursoAppDatabase {
|
|
|
1893
1924
|
const definition = await this.getProjectionDefinition(query.projectionId);
|
|
1894
1925
|
assertProjectionReadAccess(definition, readerPluginId);
|
|
1895
1926
|
const edges = await this.requireConnection().all(query.direction === "in"
|
|
1896
|
-
? `SELECT * FROM index_projection_edges
|
|
1897
|
-
|
|
1927
|
+
? `SELECT * FROM index_projection_edges
|
|
1928
|
+
WHERE projection_id = ? AND relation = ?
|
|
1929
|
+
AND (target_row_id = ? OR target_path = (
|
|
1930
|
+
SELECT source_path FROM index_projection_rows
|
|
1931
|
+
WHERE projection_id = ? AND row_id = ?
|
|
1932
|
+
)) ORDER BY ordinal`
|
|
1933
|
+
: `SELECT * FROM index_projection_edges WHERE projection_id = ? AND relation = ? AND source_row_id = ? ORDER BY ordinal`, query.projectionId, query.relation, query.rowId, ...(query.direction === "in" ? [query.projectionId, query.rowId] : []));
|
|
1898
1934
|
const rows = [];
|
|
1899
1935
|
for (const edge of edges) {
|
|
1900
1936
|
const targetId = query.direction === "in"
|
|
@@ -1933,7 +1969,7 @@ export class TursoAppDatabase {
|
|
|
1933
1969
|
this.commitQueue = committed.then(() => undefined, () => undefined);
|
|
1934
1970
|
return committed;
|
|
1935
1971
|
}
|
|
1936
|
-
async commitImmediately(statements, domains, paths = [], renamed) {
|
|
1972
|
+
async commitImmediately(statements, domains, paths = [], renamed, onCommitted) {
|
|
1937
1973
|
const revision = this.currentRevision + 1;
|
|
1938
1974
|
const change = {
|
|
1939
1975
|
revision,
|
|
@@ -1963,6 +1999,7 @@ export class TursoAppDatabase {
|
|
|
1963
1999
|
},
|
|
1964
2000
|
], "immediate");
|
|
1965
2001
|
this.currentRevision = revision;
|
|
2002
|
+
onCommitted?.();
|
|
1966
2003
|
for (const listener of this.changeListeners)
|
|
1967
2004
|
listener(clone(change));
|
|
1968
2005
|
return change;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { compileEditorAssociationGlob } from "../glob";
|
|
2
|
+
import { normalizePath } from "./path";
|
|
3
|
+
export const DEFAULT_VAULT_EXCLUDES = Object.freeze([
|
|
4
|
+
".git",
|
|
5
|
+
".jj",
|
|
6
|
+
]);
|
|
7
|
+
/** Discovery projection only; explicit resources and adapter IO stay unchanged. */
|
|
8
|
+
export function createVaultListingFilter(readPatterns) {
|
|
9
|
+
let signature;
|
|
10
|
+
let matchers = [];
|
|
11
|
+
return () => {
|
|
12
|
+
const value = readPatterns();
|
|
13
|
+
const patterns = Array.isArray(value) ? value : DEFAULT_VAULT_EXCLUDES;
|
|
14
|
+
const next = JSON.stringify(patterns);
|
|
15
|
+
if (signature !== next) {
|
|
16
|
+
matchers = patterns
|
|
17
|
+
.filter((pattern) => typeof pattern === "string")
|
|
18
|
+
.map((pattern) => compileEditorAssociationGlob(pattern, { caseSensitive: true }))
|
|
19
|
+
.filter((matcher) => matcher !== null);
|
|
20
|
+
signature = next;
|
|
21
|
+
}
|
|
22
|
+
const current = matchers;
|
|
23
|
+
return (path) => {
|
|
24
|
+
const relative = normalizePath(path).replace(/^\/+|\/+$/gu, "");
|
|
25
|
+
if (!relative)
|
|
26
|
+
return true;
|
|
27
|
+
const parts = relative.split("/");
|
|
28
|
+
for (let length = 1; length <= parts.length; length++) {
|
|
29
|
+
const ancestor = parts.slice(0, length).join("/");
|
|
30
|
+
if (current.some((matcher) => matcher.match(ancestor)))
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ChangeEvent, type DataAdapter, type Stat, type TAbstractFile } from "./fs";
|
|
2
|
+
import type { Vault } from "./vault.svelte";
|
|
3
|
+
type Snapshot = Map<string, Stat>;
|
|
4
|
+
export type VaultTreeEvent = {
|
|
5
|
+
type: "create" | "modify" | "delete" | "rename";
|
|
6
|
+
file: TAbstractFile;
|
|
7
|
+
oldPath?: string;
|
|
8
|
+
};
|
|
9
|
+
/** Scan privately; a failed or retired scan cannot mutate the live tree. */
|
|
10
|
+
export declare function scanVaultTree(adapter: DataAdapter, current: () => boolean, bounds?: {
|
|
11
|
+
entries: number;
|
|
12
|
+
bytes: number;
|
|
13
|
+
}): Promise<Snapshot | null>;
|
|
14
|
+
/** Prepare all identities/events before the synchronous tree commit. */
|
|
15
|
+
export declare function projectVaultTree(vault: Vault, previous: Record<string, TAbstractFile>, snapshot: Snapshot, hints: readonly ChangeEvent[]): {
|
|
16
|
+
files: Record<string, TAbstractFile>;
|
|
17
|
+
events: VaultTreeEvent[];
|
|
18
|
+
};
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { TFile, TFolder, } from "./fs";
|
|
2
|
+
import { dirname } from "./path";
|
|
3
|
+
const limits = { entries: 100_000, bytes: 16 * 1024 * 1024 };
|
|
4
|
+
/** Scan privately; a failed or retired scan cannot mutate the live tree. */
|
|
5
|
+
export async function scanVaultTree(adapter, current, bounds = limits) {
|
|
6
|
+
const snapshot = new Map([
|
|
7
|
+
["/", { type: "folder", ctime: 0, mtime: 0, size: 0 }],
|
|
8
|
+
]);
|
|
9
|
+
const folders = ["/"];
|
|
10
|
+
let bytes = 96;
|
|
11
|
+
const encoder = new TextEncoder();
|
|
12
|
+
for (let index = 0; index < folders.length; index++) {
|
|
13
|
+
if (!current())
|
|
14
|
+
return null;
|
|
15
|
+
const parent = folders[index];
|
|
16
|
+
const listed = await adapter.list(parent);
|
|
17
|
+
for (const [type, names] of [
|
|
18
|
+
["file", listed.files],
|
|
19
|
+
["folder", listed.folders],
|
|
20
|
+
]) {
|
|
21
|
+
for (const name of names) {
|
|
22
|
+
if (!current())
|
|
23
|
+
return null;
|
|
24
|
+
if (!name || name === "." || name === ".." || /[/\\\0]/u.test(name))
|
|
25
|
+
throw new Error("Invalid vault snapshot child path");
|
|
26
|
+
const path = parent === "/" ? name : `${parent}/${name}`;
|
|
27
|
+
bytes += encoder.encode(path).byteLength + 96;
|
|
28
|
+
if (snapshot.has(path) ||
|
|
29
|
+
snapshot.size >= bounds.entries ||
|
|
30
|
+
bytes > bounds.bytes)
|
|
31
|
+
throw new Error("Vault reconciliation snapshot exceeds its bounds or duplicates a path");
|
|
32
|
+
const stat = await adapter.stat(path);
|
|
33
|
+
if (!stat ||
|
|
34
|
+
stat.type !== type ||
|
|
35
|
+
![stat.ctime, stat.mtime, stat.size].every(Number.isFinite) ||
|
|
36
|
+
stat.size < 0)
|
|
37
|
+
throw new Error(`Incomplete vault reconciliation snapshot: ${path}`);
|
|
38
|
+
snapshot.set(path, { ...stat });
|
|
39
|
+
if (type === "folder")
|
|
40
|
+
folders.push(path);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return current() ? snapshot : null;
|
|
45
|
+
}
|
|
46
|
+
function validPath(path) {
|
|
47
|
+
return (!!path &&
|
|
48
|
+
!/[\\\0]/u.test(path) &&
|
|
49
|
+
path.split("/").every((part) => !!part && part !== "." && part !== ".."));
|
|
50
|
+
}
|
|
51
|
+
function depth(path) {
|
|
52
|
+
return path.split("/").length;
|
|
53
|
+
}
|
|
54
|
+
function sameType(file, stat) {
|
|
55
|
+
return stat.type === "file" ? file instanceof TFile : file instanceof TFolder;
|
|
56
|
+
}
|
|
57
|
+
/** Prepare all identities/events before the synchronous tree commit. */
|
|
58
|
+
export function projectVaultTree(vault, previous, snapshot, hints) {
|
|
59
|
+
const renamed = new Map();
|
|
60
|
+
const forced = new Set();
|
|
61
|
+
for (const hint of hints) {
|
|
62
|
+
if (!validPath(hint.path) ||
|
|
63
|
+
(hint.previousPath !== undefined && !validPath(hint.previousPath)))
|
|
64
|
+
throw new Error("Invalid vault reconciliation hint");
|
|
65
|
+
if (hint.type === "modify")
|
|
66
|
+
forced.add(hint.path);
|
|
67
|
+
const old = hint.previousPath;
|
|
68
|
+
if (!old || old === hint.path || snapshot.has(old) || previous[hint.path])
|
|
69
|
+
continue;
|
|
70
|
+
const source = previous[old], target = snapshot.get(hint.path);
|
|
71
|
+
if (!source || !target || !sameType(source, target))
|
|
72
|
+
continue;
|
|
73
|
+
for (const path of Object.keys(previous)) {
|
|
74
|
+
if (path !== old &&
|
|
75
|
+
!(source instanceof TFolder && path.startsWith(`${old}/`)))
|
|
76
|
+
continue;
|
|
77
|
+
const destination = hint.path + path.slice(old.length);
|
|
78
|
+
const stat = snapshot.get(destination);
|
|
79
|
+
if (stat &&
|
|
80
|
+
!previous[destination] &&
|
|
81
|
+
!snapshot.has(path) &&
|
|
82
|
+
sameType(previous[path], stat)) {
|
|
83
|
+
const existing = renamed.get(destination);
|
|
84
|
+
if (existing && existing !== path)
|
|
85
|
+
throw new Error("Ambiguous vault rename hints");
|
|
86
|
+
renamed.set(destination, path);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const files = Object.create(null);
|
|
91
|
+
const events = [];
|
|
92
|
+
const consumed = new Set();
|
|
93
|
+
const paths = [...snapshot.keys()].sort((a, b) => depth(a) - depth(b) || a.localeCompare(b));
|
|
94
|
+
for (const path of paths) {
|
|
95
|
+
const stat = snapshot.get(path);
|
|
96
|
+
const oldPath = renamed.get(path) ?? path;
|
|
97
|
+
const existing = previous[oldPath];
|
|
98
|
+
const file = sameType(existing, stat)
|
|
99
|
+
? existing
|
|
100
|
+
: stat.type === "file"
|
|
101
|
+
? new TFile(path, { ...stat }, null, vault)
|
|
102
|
+
: new TFolder(path, [], null, vault);
|
|
103
|
+
if (consumed.has(file))
|
|
104
|
+
throw new Error("Duplicate vault resource identity");
|
|
105
|
+
consumed.add(file);
|
|
106
|
+
files[path] = file;
|
|
107
|
+
if (path === "/")
|
|
108
|
+
continue;
|
|
109
|
+
if (file !== existing)
|
|
110
|
+
events.push({ type: "create", file });
|
|
111
|
+
else if (oldPath !== path)
|
|
112
|
+
events.push({ type: "rename", file, oldPath });
|
|
113
|
+
else if (file instanceof TFile &&
|
|
114
|
+
(forced.has(path) ||
|
|
115
|
+
file.stat.mtime !== stat.mtime ||
|
|
116
|
+
file.stat.ctime !== stat.ctime ||
|
|
117
|
+
file.stat.size !== stat.size))
|
|
118
|
+
events.push({ type: "modify", file });
|
|
119
|
+
}
|
|
120
|
+
const deleted = Object.values(previous).filter((file) => !consumed.has(file));
|
|
121
|
+
deleted.sort((a, b) => depth(b.path) - depth(a.path) || a.path.localeCompare(b.path));
|
|
122
|
+
events.unshift(...deleted.map((file) => ({ type: "delete", file })));
|
|
123
|
+
// Nothing above mutates canonical nodes. Commit after all validation succeeds.
|
|
124
|
+
for (const path of paths) {
|
|
125
|
+
const file = files[path];
|
|
126
|
+
file.path = path === "/" ? "" : path;
|
|
127
|
+
file.parent = path === "/" ? null : files[dirname(path)];
|
|
128
|
+
if (file instanceof TFile)
|
|
129
|
+
Object.assign(file.stat, snapshot.get(path));
|
|
130
|
+
else if (file instanceof TFolder)
|
|
131
|
+
file.children.splice(0);
|
|
132
|
+
}
|
|
133
|
+
for (const path of paths)
|
|
134
|
+
if (path !== "/")
|
|
135
|
+
files[path].parent.children.push(files[path]);
|
|
136
|
+
return { files, events };
|
|
137
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import type { NativeTasks } from "../native-tasks";
|
|
1
2
|
import { type AppDatabase, type AppDatabaseCapabilities, type AppDatabaseDescriptor, type AppDatabaseProvider, type RuntimeTarget } from "./app-database";
|
|
3
|
+
import type { NativeBackgroundIndexing } from "../native-background-indexing";
|
|
4
|
+
import type { NativeHistoryIndex } from "../native-history-index";
|
|
5
|
+
import type { NativeEditingIntents } from "../native-editing-intents";
|
|
2
6
|
import type { VaultAdapter } from "./fs";
|
|
3
7
|
import { type VaultProfile } from "./vault-state";
|
|
4
8
|
export type VaultSessionAppDatabaseStatus = "ready" | "blocked";
|
|
@@ -22,6 +26,10 @@ export interface VaultSession {
|
|
|
22
26
|
profile?: VaultProfile;
|
|
23
27
|
vaultAdapter: VaultAdapter;
|
|
24
28
|
appDatabase?: AppDatabase;
|
|
29
|
+
backgroundIndexing?: NativeBackgroundIndexing;
|
|
30
|
+
nativeHistoryIndex?: NativeHistoryIndex;
|
|
31
|
+
nativeEditingIntents?: NativeEditingIntents;
|
|
32
|
+
nativeTasks?: NativeTasks;
|
|
25
33
|
appDatabaseState: VaultSessionAppDatabaseState;
|
|
26
34
|
awaitAppDatabase?: (options?: {
|
|
27
35
|
signal?: AbortSignal;
|
|
@@ -128,6 +128,12 @@ async function createPreferredAppDatabase(vaultId, runtime, provider) {
|
|
|
128
128
|
await database.open();
|
|
129
129
|
return { appDatabase: database, state: readyState(database, false) };
|
|
130
130
|
}
|
|
131
|
+
if (runtime === "tauri-desktop") {
|
|
132
|
+
if (!provider)
|
|
133
|
+
throw new Error("tauri-desktop requires an AppDatabaseProvider");
|
|
134
|
+
const database = await provider.open({ vaultId, runtime, role: "direct" });
|
|
135
|
+
return { appDatabase: database, state: readyState(database, false) };
|
|
136
|
+
}
|
|
131
137
|
if (runtime === "deno-desktop") {
|
|
132
138
|
const database = provider
|
|
133
139
|
? await provider.open({ vaultId, runtime, role: "direct" })
|
|
@@ -138,9 +144,11 @@ async function createPreferredAppDatabase(vaultId, runtime, provider) {
|
|
|
138
144
|
});
|
|
139
145
|
return { appDatabase: database, state: readyState(database, false) };
|
|
140
146
|
}
|
|
141
|
-
if (runtime === "
|
|
147
|
+
if (runtime === "node-server" ||
|
|
148
|
+
runtime === "deno-server" ||
|
|
149
|
+
runtime === "rust-server") {
|
|
142
150
|
if (!provider) {
|
|
143
|
-
throw new Error("
|
|
151
|
+
throw new Error("server-hosted vaults require an AppDatabaseProvider");
|
|
144
152
|
}
|
|
145
153
|
const database = await provider.open({
|
|
146
154
|
vaultId,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { FileEditingSessionRegistry } from "../file-editing-sessions";
|
|
1
2
|
import { EventDispatcher } from "../events";
|
|
2
3
|
import {
|
|
3
4
|
FileCache,
|
|
4
5
|
TFile,
|
|
5
6
|
TFolder,
|
|
6
7
|
type DataAdapter,
|
|
8
|
+
type ChangeEvent,
|
|
7
9
|
type DataWriteOptions,
|
|
8
10
|
type TAbstractFile,
|
|
9
11
|
} from "./fs";
|
|
@@ -19,6 +21,9 @@ export declare const ENOTDIR: {
|
|
|
19
21
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
20
22
|
stackTraceLimit: number;
|
|
21
23
|
};
|
|
24
|
+
export interface VaultListingOptions {
|
|
25
|
+
listingExclusions?: () => readonly string[];
|
|
26
|
+
}
|
|
22
27
|
export interface VaultGlobOptions {
|
|
23
28
|
caseSensitive?: boolean;
|
|
24
29
|
}
|
|
@@ -42,9 +47,18 @@ export declare class Vault extends EventDispatcher<{
|
|
|
42
47
|
readonly adapter: DataAdapter;
|
|
43
48
|
private files;
|
|
44
49
|
private filesByName;
|
|
50
|
+
private mutationTail;
|
|
51
|
+
private mutate;
|
|
52
|
+
private mutateFile;
|
|
53
|
+
private reconcileNow;
|
|
45
54
|
configDir: string;
|
|
46
55
|
readonly cache: FileCache;
|
|
47
|
-
|
|
56
|
+
private readonly listingFilter;
|
|
57
|
+
constructor(
|
|
58
|
+
adapter: DataAdapter,
|
|
59
|
+
operations?: Pick<FileEditingSessionRegistry, "track">,
|
|
60
|
+
options?: VaultListingOptions,
|
|
61
|
+
);
|
|
48
62
|
private getPath;
|
|
49
63
|
getName(): string;
|
|
50
64
|
exists(path: string, sensitive?: boolean): Promise<boolean>;
|
|
@@ -52,6 +66,23 @@ export declare class Vault extends EventDispatcher<{
|
|
|
52
66
|
list(path?: string): Promise<import("./fs").ListedFiles>;
|
|
53
67
|
load(): Promise<void>;
|
|
54
68
|
reload(): Promise<TFolder>;
|
|
69
|
+
reconcile(
|
|
70
|
+
hints?: readonly ChangeEvent[],
|
|
71
|
+
current?: () => boolean,
|
|
72
|
+
context?: Record<string, unknown>,
|
|
73
|
+
): Promise<void>;
|
|
74
|
+
private deleteNow;
|
|
75
|
+
private trashNow;
|
|
76
|
+
private atomicWriteNow;
|
|
77
|
+
private renameNow;
|
|
78
|
+
private modifyNow;
|
|
79
|
+
private modifyIfUnchangedNow;
|
|
80
|
+
private modifyBinaryNow;
|
|
81
|
+
private processNow;
|
|
82
|
+
private appendNow;
|
|
83
|
+
private appendBinaryNow;
|
|
84
|
+
private copyNow;
|
|
85
|
+
private recordFile;
|
|
55
86
|
private addToFileNameIndex;
|
|
56
87
|
private removeFromFileNameIndex;
|
|
57
88
|
private rebuildFileNameIndex;
|
|
@@ -72,6 +103,12 @@ export declare class Vault extends EventDispatcher<{
|
|
|
72
103
|
data: string,
|
|
73
104
|
options?: DataWriteOptions,
|
|
74
105
|
): Promise<TFile>;
|
|
106
|
+
get supportsExclusiveCreate(): boolean;
|
|
107
|
+
createExclusive(
|
|
108
|
+
path: string,
|
|
109
|
+
data: string | ArrayBuffer,
|
|
110
|
+
options?: DataWriteOptions,
|
|
111
|
+
): Promise<TFile>;
|
|
75
112
|
/**
|
|
76
113
|
* Create a new binary file inside the vault.
|
|
77
114
|
*
|
|
@@ -103,9 +140,13 @@ export declare class Vault extends EventDispatcher<{
|
|
|
103
140
|
* children
|
|
104
141
|
* @public
|
|
105
142
|
*/
|
|
106
|
-
delete(
|
|
107
|
-
|
|
108
|
-
|
|
143
|
+
delete(
|
|
144
|
+
file: TAbstractFile,
|
|
145
|
+
force?: boolean,
|
|
146
|
+
context?: {
|
|
147
|
+
operationOrigin?: string;
|
|
148
|
+
},
|
|
149
|
+
): Promise<void>;
|
|
109
150
|
trash(file: TAbstractFile, system?: boolean): Promise<void>;
|
|
110
151
|
/**
|
|
111
152
|
* Read a plaintext file that is stored inside the vault, directly from disk.
|
|
@@ -147,6 +188,13 @@ export declare class Vault extends EventDispatcher<{
|
|
|
147
188
|
* @public
|
|
148
189
|
*/
|
|
149
190
|
modify(file: TFile, data: string, options?: DataWriteOptions): Promise<void>;
|
|
191
|
+
get supportsConditionalTextWrites(): boolean;
|
|
192
|
+
modifyIfUnchanged(
|
|
193
|
+
file: TFile,
|
|
194
|
+
data: string,
|
|
195
|
+
expected: string | null,
|
|
196
|
+
options?: DataWriteOptions,
|
|
197
|
+
): Promise<void>;
|
|
150
198
|
/**
|
|
151
199
|
* Modify the contents of a binary file.
|
|
152
200
|
*
|
|
@@ -241,6 +289,7 @@ export declare class Vault extends EventDispatcher<{
|
|
|
241
289
|
getFiles(): TFile[];
|
|
242
290
|
/**
|
|
243
291
|
* Iterate loaded files without allocating a second vault-sized collection.
|
|
292
|
+
*
|
|
244
293
|
* @public
|
|
245
294
|
*/
|
|
246
295
|
iterateFiles(): IterableIterator<TFile>;
|