@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,9 +1,12 @@
|
|
|
1
|
+
import type { HostMetadataStore, AppDatabaseMetaTransaction, AppDatabaseMetaTransactionResult } from "../metadata-store";
|
|
2
|
+
export type { HostMetadataStore, AppDatabaseMetaCheck, AppDatabaseMetaMutation, AppDatabaseMetaTransaction, AppDatabaseMetaTransactionResult, } from "../metadata-store";
|
|
1
3
|
import { type SearchEmbeddingRuntimeStatus, type SearchEmbeddingProvider, type SearchEmbeddingProviderConfig } from "./search-embedding-provider";
|
|
2
4
|
import { type SearchQueryEvaluationOptions, type SearchQueryTextRangeOptions } from "./search-query-evaluator";
|
|
3
5
|
import { type IndexProjectionDefinitionRecord, type IndexProjectionEdgeRecord, type IndexQuery, type IndexQueryResult, type IndexRelatedQuery, type IndexProjectionRowRecord, type IndexProjectionSourceRecord, type IndexProjectionValueRecord, type MarkProjectionSourceErrorInput, type ReplaceProjectionSourceInput } from "./index-projection";
|
|
4
6
|
import { type AppDatabaseLinkKind, type AppDatabaseTaskChildQuery, type AppDatabaseTaskQuery, type AppDatabaseTaskRecord } from "./task-projection";
|
|
5
7
|
export type { SearchEmbeddingProviderConfig, SearchEmbeddingRuntimeStatus, } from "./search-embedding-provider";
|
|
6
|
-
|
|
8
|
+
import type { RuntimeTarget } from "./runtime";
|
|
9
|
+
export { isNativeDesktopRuntime, type RuntimeTarget } from "./runtime";
|
|
7
10
|
export interface LocalSearchQueryEnhancementProviderConfig {
|
|
8
11
|
kind: "local-model";
|
|
9
12
|
queryExpansionModelId: string;
|
|
@@ -115,7 +118,10 @@ export interface AppDatabaseFileRecord {
|
|
|
115
118
|
mtime: number;
|
|
116
119
|
size: number;
|
|
117
120
|
hash: string;
|
|
118
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* Parser signature from the indexed metadata row when requested as a
|
|
123
|
+
* manifest.
|
|
124
|
+
*/
|
|
119
125
|
parserVersion?: string;
|
|
120
126
|
indexed: boolean;
|
|
121
127
|
deleted?: boolean;
|
|
@@ -278,23 +284,6 @@ export interface AppDatabaseMetaScanPage<T = unknown> {
|
|
|
278
284
|
entries: AppDatabaseMetaEntry<T>[];
|
|
279
285
|
nextCursor?: string;
|
|
280
286
|
}
|
|
281
|
-
export interface AppDatabaseMetaCheck {
|
|
282
|
-
key: string;
|
|
283
|
-
exists?: boolean;
|
|
284
|
-
value?: unknown;
|
|
285
|
-
}
|
|
286
|
-
export interface AppDatabaseMetaMutation {
|
|
287
|
-
key: string;
|
|
288
|
-
value?: unknown;
|
|
289
|
-
delete?: boolean;
|
|
290
|
-
}
|
|
291
|
-
export interface AppDatabaseMetaTransaction {
|
|
292
|
-
checks?: AppDatabaseMetaCheck[];
|
|
293
|
-
mutations: AppDatabaseMetaMutation[];
|
|
294
|
-
}
|
|
295
|
-
export interface AppDatabaseMetaTransactionResult {
|
|
296
|
-
applied: boolean;
|
|
297
|
-
}
|
|
298
287
|
export type AppDatabaseChangeListener = (change: AppDatabaseChangeSet) => void;
|
|
299
288
|
export interface SearchDocumentRecord {
|
|
300
289
|
path: string;
|
|
@@ -607,7 +596,7 @@ export interface AppDatabaseMemoryJobUpdateInput {
|
|
|
607
596
|
now: number;
|
|
608
597
|
patch: Pick<AppDatabaseMemoryJobRecord, "status"> & Partial<Pick<AppDatabaseMemoryJobRecord, "cursor" | "leaseUntil" | "finishedAt" | "errorCode">>;
|
|
609
598
|
}
|
|
610
|
-
export interface AppDatabase {
|
|
599
|
+
export interface AppDatabase extends HostMetadataStore {
|
|
611
600
|
readonly kind: AppDatabaseKind;
|
|
612
601
|
readonly vaultId: string;
|
|
613
602
|
readonly descriptor: AppDatabaseDescriptor;
|
|
@@ -620,10 +609,7 @@ export interface AppDatabase {
|
|
|
620
609
|
getSearchEmbeddingProvider(): Promise<SearchEmbeddingProviderConfig | null>;
|
|
621
610
|
getSearchEmbeddingRuntimeStatus(): Promise<SearchEmbeddingRuntimeStatus | null>;
|
|
622
611
|
getSearchIndexStats(): Promise<AppDatabaseSearchIndexStats>;
|
|
623
|
-
getMeta<T = unknown>(key: string): Promise<T | undefined>;
|
|
624
|
-
setMeta(key: string, value: unknown): Promise<void>;
|
|
625
612
|
scanMeta<T = unknown>(query: AppDatabaseMetaScanQuery): Promise<AppDatabaseMetaScanPage<T>>;
|
|
626
|
-
transactMeta(transaction: AppDatabaseMetaTransaction): Promise<AppDatabaseMetaTransactionResult>;
|
|
627
613
|
getNotebookState(sourcePath: string): Promise<AppDatabaseNotebookState | undefined>;
|
|
628
614
|
setNotebookState(sourcePath: string, state: AppDatabaseNotebookState): Promise<void>;
|
|
629
615
|
deleteNotebookState(sourcePath: string): Promise<void>;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { rebindSearchDocument } from "./search-embedding-record";
|
|
1
2
|
import { canCollectSearchQueryPropertyNames, canCollectSearchQueryTerms, collectSearchQueryPropertyNames, collectSearchQueryTerms, parseSearchQueryAst, } from "../search-query";
|
|
2
3
|
import { cosineSimilarity, createSearchEmbeddingProvider, } from "./search-embedding-provider";
|
|
3
4
|
import { evaluateSearchQueryForDocument, findSearchQueryRangesInText, isStructuredSearchQuery, } from "./search-query-evaluator";
|
|
4
5
|
import { assertProjectionReadAccess, assertProjectionWriteAccess, evaluateProjectionQuery, indexedValuesForRow, MAX_PROJECTION_ROWS_PER_SOURCE, projectionIndexStatus, PUBLIC_TASKS_PROJECTION_ID, sourceIsCurrent, } from "./index-projection";
|
|
5
6
|
import { taskQueryToIndexQuery, TASK_PROJECTION_FIELDS, TASK_PROJECTION_VERSION, } from "./task-projection";
|
|
7
|
+
export { isNativeDesktopRuntime } from "./runtime";
|
|
6
8
|
export function supportsSearchQueryEnhancementRuntime(runtimeTarget) {
|
|
7
9
|
return runtimeTarget === "deno-desktop";
|
|
8
10
|
}
|
|
@@ -245,7 +247,16 @@ function buildSearchChunks(path, content, metadata) {
|
|
|
245
247
|
const chunkEnd = remaining > metadata.chunking.targetChars
|
|
246
248
|
? findBestCutoff(breakPoints, chunkStart, targetEnd, metadata.chunking.breakpointWindowChars, metadata.chunking.breakpointDecay, codeFences)
|
|
247
249
|
: content.length;
|
|
248
|
-
|
|
250
|
+
let safeEnd = chunkEnd > chunkStart ? chunkEnd : targetEnd;
|
|
251
|
+
// Search offsets use UTF-16, but chunks must remain complete Unicode text.
|
|
252
|
+
if (safeEnd > 0 &&
|
|
253
|
+
safeEnd < content.length &&
|
|
254
|
+
content.charCodeAt(safeEnd - 1) >= 0xd800 &&
|
|
255
|
+
content.charCodeAt(safeEnd - 1) <= 0xdbff &&
|
|
256
|
+
content.charCodeAt(safeEnd) >= 0xdc00 &&
|
|
257
|
+
content.charCodeAt(safeEnd) <= 0xdfff) {
|
|
258
|
+
safeEnd += 1;
|
|
259
|
+
}
|
|
249
260
|
const chunk = createChunk(path, content, metadata, chunkStart, safeEnd);
|
|
250
261
|
if (!chunk) {
|
|
251
262
|
if (safeEnd <= chunkStart) {
|
|
@@ -554,6 +565,18 @@ function renameChunkId(chunkId, oldPath, newPath) {
|
|
|
554
565
|
}
|
|
555
566
|
return `${newPath}${chunkId.slice(oldPath.length)}`;
|
|
556
567
|
}
|
|
568
|
+
function searchSnippetBounds(text, start, end) {
|
|
569
|
+
const splitsPair = (offset) => offset > 0 &&
|
|
570
|
+
offset < text.length &&
|
|
571
|
+
text.charCodeAt(offset - 1) >= 0xd800 &&
|
|
572
|
+
text.charCodeAt(offset - 1) <= 0xdbff &&
|
|
573
|
+
text.charCodeAt(offset) >= 0xdc00 &&
|
|
574
|
+
text.charCodeAt(offset) <= 0xdfff;
|
|
575
|
+
return [
|
|
576
|
+
splitsPair(start) ? start - 1 : start,
|
|
577
|
+
splitsPair(end) ? end + 1 : end,
|
|
578
|
+
];
|
|
579
|
+
}
|
|
557
580
|
function buildFieldSnippet(field, text, ranges, snippetLength) {
|
|
558
581
|
if (!ranges.length || !text.length)
|
|
559
582
|
return [];
|
|
@@ -569,8 +592,9 @@ function buildFieldSnippet(field, text, ranges, snippetLength) {
|
|
|
569
592
|
}
|
|
570
593
|
const focus = ranges[0];
|
|
571
594
|
const padding = Math.max(16, Math.floor(snippetLength / 3));
|
|
572
|
-
const
|
|
573
|
-
const
|
|
595
|
+
const initialStart = Math.max(0, focus.start - padding);
|
|
596
|
+
const initialEnd = Math.min(text.length, Math.max(initialStart + snippetLength, focus.end + padding));
|
|
597
|
+
const [start, end] = searchSnippetBounds(text, initialStart, initialEnd);
|
|
574
598
|
return [
|
|
575
599
|
{
|
|
576
600
|
field,
|
|
@@ -612,8 +636,9 @@ function buildContentSnippets(text, ranges, snippetLength) {
|
|
|
612
636
|
return { ...snippet, ranges };
|
|
613
637
|
}
|
|
614
638
|
const focus = ranges[0];
|
|
615
|
-
const
|
|
616
|
-
const
|
|
639
|
+
const initialStart = Math.max(0, focus.start - Math.floor(snippetLength / 3));
|
|
640
|
+
const initialEnd = Math.min(snippet.text.length, Math.max(initialStart + snippetLength, focus.end + Math.floor(snippetLength / 3)));
|
|
641
|
+
const [start, end] = searchSnippetBounds(snippet.text, initialStart, initialEnd);
|
|
617
642
|
return {
|
|
618
643
|
field: "content",
|
|
619
644
|
text: snippet.text.slice(start, end),
|
|
@@ -1335,7 +1360,7 @@ export class MemoryAppDatabase {
|
|
|
1335
1360
|
...clone(input.patch),
|
|
1336
1361
|
ownerId: input.patch.status === "running" ? existing.ownerId : undefined,
|
|
1337
1362
|
leaseUntil: input.patch.status === "running"
|
|
1338
|
-
? input.patch.leaseUntil ?? existing.leaseUntil
|
|
1363
|
+
? (input.patch.leaseUntil ?? existing.leaseUntil)
|
|
1339
1364
|
: undefined,
|
|
1340
1365
|
};
|
|
1341
1366
|
this.memoryJobs.set(updated.id, updated);
|
|
@@ -1898,13 +1923,15 @@ export class MemoryAppDatabase {
|
|
|
1898
1923
|
const targetProjection = query.direction === "in"
|
|
1899
1924
|
? edge.projectionId
|
|
1900
1925
|
: (edge.targetProjectionId ?? query.projectionId);
|
|
1926
|
+
const targetDefinition = this.projections.get(targetProjection);
|
|
1927
|
+
assertProjectionReadAccess(targetDefinition, readerPluginId);
|
|
1901
1928
|
const row = query.direction === "in"
|
|
1902
1929
|
? this.projectionRows.get(this.projectionRowKey(targetProjection, edge.sourceRowId))
|
|
1903
1930
|
: edge.targetRowId
|
|
1904
1931
|
? this.projectionRows.get(this.projectionRowKey(targetProjection, edge.targetRowId))
|
|
1905
1932
|
: [...this.projectionRows.values()].find((candidate) => candidate.projectionId === targetProjection &&
|
|
1906
1933
|
candidate.sourcePath === edge.targetPath);
|
|
1907
|
-
if (!row)
|
|
1934
|
+
if (!row || !this.currentProjectionData(row, targetDefinition))
|
|
1908
1935
|
continue;
|
|
1909
1936
|
if (query.targetWhere &&
|
|
1910
1937
|
!evaluateProjectionQuery([row], { where: query.targetWhere }, 0, "ready").rows.length) {
|
|
@@ -2103,28 +2130,7 @@ export class MemoryAppDatabase {
|
|
|
2103
2130
|
}
|
|
2104
2131
|
}
|
|
2105
2132
|
rebindSearchDocumentForProvider(document) {
|
|
2106
|
-
|
|
2107
|
-
if (!provider || !document.chunks?.length) {
|
|
2108
|
-
return clone(document);
|
|
2109
|
-
}
|
|
2110
|
-
return {
|
|
2111
|
-
...clone(document),
|
|
2112
|
-
chunks: document.chunks.map((chunk) => {
|
|
2113
|
-
if (!chunk.text.trim().length) {
|
|
2114
|
-
return clone(chunk);
|
|
2115
|
-
}
|
|
2116
|
-
return {
|
|
2117
|
-
...clone(chunk),
|
|
2118
|
-
embedding: {
|
|
2119
|
-
status: "pending",
|
|
2120
|
-
modelId: provider.config.modelId ?? "lapis/token-hash-v0",
|
|
2121
|
-
modelVersion: provider.config.modelVersion,
|
|
2122
|
-
dimensions: provider.config.dimensions,
|
|
2123
|
-
dirty: true,
|
|
2124
|
-
},
|
|
2125
|
-
};
|
|
2126
|
-
}),
|
|
2127
|
-
};
|
|
2133
|
+
return rebindSearchDocument(document, this.searchEmbeddingProvider?.config ?? null);
|
|
2128
2134
|
}
|
|
2129
2135
|
toState() {
|
|
2130
2136
|
return {
|
|
@@ -2154,9 +2160,7 @@ export class MemoryAppDatabase {
|
|
|
2154
2160
|
projectionRevision: this.projectionRevision,
|
|
2155
2161
|
memorySources: [...this.memorySources.values()].map((value) => clone(value)),
|
|
2156
2162
|
memoryCandidates: [...this.memoryCandidates.values()].map((value) => clone(value)),
|
|
2157
|
-
memoryCandidateOrigins: [
|
|
2158
|
-
...this.memoryCandidateOrigins.values(),
|
|
2159
|
-
].flatMap((origins) => origins.map((origin) => clone(origin))),
|
|
2163
|
+
memoryCandidateOrigins: [...this.memoryCandidateOrigins.values()].flatMap((origins) => origins.map((origin) => clone(origin))),
|
|
2160
2164
|
memoryRecallSignals: [...this.memoryRecallSignals.values()].map((value) => clone(value)),
|
|
2161
2165
|
memoryJobs: [...this.memoryJobs.values()].map((value) => clone(value)),
|
|
2162
2166
|
};
|
|
@@ -2252,6 +2256,9 @@ export class MemoryAppDatabase {
|
|
|
2252
2256
|
errorChunkCount += 1;
|
|
2253
2257
|
lastError ??= embedding.error ?? null;
|
|
2254
2258
|
break;
|
|
2259
|
+
default:
|
|
2260
|
+
pendingChunkCount += 1;
|
|
2261
|
+
break;
|
|
2255
2262
|
}
|
|
2256
2263
|
}
|
|
2257
2264
|
return {
|
|
@@ -2286,7 +2293,7 @@ export class MemoryAppDatabase {
|
|
|
2286
2293
|
this.searchIndexStats.readyChunkCount += stats.readyChunkCount;
|
|
2287
2294
|
this.searchIndexStats.pendingChunkCount += stats.pendingChunkCount;
|
|
2288
2295
|
this.searchIndexStats.errorChunkCount += stats.errorChunkCount;
|
|
2289
|
-
if (stats.lastError) {
|
|
2296
|
+
if (stats.lastError !== null) {
|
|
2290
2297
|
this.searchIndexStats.lastError = stats.lastError;
|
|
2291
2298
|
this.searchIndexLastErrorPath = path;
|
|
2292
2299
|
}
|
|
@@ -2301,7 +2308,7 @@ export class MemoryAppDatabase {
|
|
|
2301
2308
|
this.searchIndexStats.lastError = null;
|
|
2302
2309
|
this.searchIndexLastErrorPath = null;
|
|
2303
2310
|
for (const [nextPath, nextStats] of this.searchIndexStatsByPath) {
|
|
2304
|
-
if (nextPath === path ||
|
|
2311
|
+
if (nextPath === path || nextStats.lastError === null) {
|
|
2305
2312
|
continue;
|
|
2306
2313
|
}
|
|
2307
2314
|
this.searchIndexStats.lastError = nextStats.lastError;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import type { AppDatabaseChangeSet, AppDatabaseNotificationSeverity } from "./app-database";
|
|
9
9
|
import type { ChangeEvent, AsyncResourceAdapter, DataWriteOptions, ListedFiles, NativeWatchAdapter, NativeWatchSubscription, Stat, VaultAdapter, VaultAdapterCapabilities, VaultReadHandle, AtomicDataWriteOptions, VaultIdentityAdapter, WatchErrorEvent, WatchOptions } from "./fs";
|
|
10
10
|
import { type KeyValueStore, type VaultProfile } from "./vault-state";
|
|
11
|
-
export type NativeDesktopRuntime = "deno-desktop";
|
|
11
|
+
export type NativeDesktopRuntime = "deno-desktop" | "tauri-desktop";
|
|
12
12
|
export type NativeDesktopPlatformOs = "macos" | "windows" | "linux" | "unknown";
|
|
13
13
|
export interface NativeDesktopPlatformInfo {
|
|
14
14
|
readonly runtime: NativeDesktopRuntime;
|
|
@@ -18,7 +18,7 @@ export interface NativeDesktopPlatformInfo {
|
|
|
18
18
|
readonly appVersion?: string;
|
|
19
19
|
readonly packaged?: boolean;
|
|
20
20
|
}
|
|
21
|
-
export type NativeDesktopCapabilityId = "resource" | "database" | "search" | "notebook" | "language-service" | "model" | "plugin-sidecar" | "plugin-assets" | "file-watch" | "notifications" | "file-system-actions" | "nostr-signer" | "agent-runtime" | "terminal-runtime" | "secure-storage";
|
|
21
|
+
export type NativeDesktopCapabilityId = "system-trash" | "exclusive-create" | "conditional-text-write" | "resource" | "database" | "search" | "notebook" | "language-service" | "model" | "plugin-sidecar" | "plugin-assets" | "file-watch" | "notifications" | "file-system-actions" | "nostr-signer" | "agent-runtime" | "terminal-runtime" | "secure-storage";
|
|
22
22
|
export type NativeAgentProcessMessage = {
|
|
23
23
|
processId: string;
|
|
24
24
|
type: "stdout" | "stderr" | "exit";
|
|
@@ -151,8 +151,10 @@ export declare class NativeDesktopVaultAdapter implements VaultAdapter, VaultIde
|
|
|
151
151
|
stat(normalizedPath: string): Promise<Stat | null>;
|
|
152
152
|
read(normalizedPath: string): Promise<string>;
|
|
153
153
|
readBinary(normalizedPath: string): Promise<ArrayBuffer>;
|
|
154
|
+
writeExclusive(normalizedPath: string, data: string | ArrayBuffer, options?: DataWriteOptions): Promise<void>;
|
|
154
155
|
openRead(normalizedPath: string): Promise<VaultReadHandle>;
|
|
155
156
|
atomicWrite(normalizedPath: string, source: ReadableStream<Uint8Array>, options?: AtomicDataWriteOptions): Promise<void>;
|
|
157
|
+
writeTextIfUnchanged(normalizedPath: string, data: string, expected: string | null, options?: DataWriteOptions): Promise<void>;
|
|
156
158
|
write(normalizedPath: string, data: string, options?: DataWriteOptions): Promise<void>;
|
|
157
159
|
writeBinary(normalizedPath: string, data: ArrayBuffer, options?: DataWriteOptions): Promise<void>;
|
|
158
160
|
append(normalizedPath: string, data: string, options?: DataWriteOptions): Promise<void>;
|
|
@@ -169,7 +171,7 @@ export declare class NativeDesktopVaultAdapter implements VaultAdapter, VaultIde
|
|
|
169
171
|
copy(normalizedPath: string, normalizedNewPath: string): Promise<void>;
|
|
170
172
|
getResourcePath(normalizedPath: string): string;
|
|
171
173
|
getResourceUrl(normalizedPath: string): Promise<string>;
|
|
172
|
-
trashSystem(): Promise<boolean>;
|
|
174
|
+
trashSystem(normalizedPath: string): Promise<boolean>;
|
|
173
175
|
trashLocal(normalizedPath: string): Promise<void>;
|
|
174
176
|
}
|
|
175
177
|
export declare function pickNativeDesktopVault(options?: NativeVaultOptions): Promise<{
|
|
@@ -237,13 +237,15 @@ export class NativeDesktopVaultAdapter {
|
|
|
237
237
|
}
|
|
238
238
|
getCapabilities() {
|
|
239
239
|
return {
|
|
240
|
+
exclusiveCreate: hasNativeDesktopCapability("exclusive-create"),
|
|
241
|
+
conditionalTextWrite: hasNativeDesktopCapability("conditional-text-write"),
|
|
240
242
|
persistent: true,
|
|
241
243
|
userVisibleFiles: true,
|
|
242
244
|
requiresPermission: false,
|
|
243
245
|
nativeWatch: hasNativeDesktopCapability("file-watch") ||
|
|
244
246
|
typeof getNativeDesktopBridge()?.watch === "function",
|
|
245
247
|
resourceUrls: true,
|
|
246
|
-
systemTrash:
|
|
248
|
+
systemTrash: hasNativeDesktopCapability("system-trash"),
|
|
247
249
|
};
|
|
248
250
|
}
|
|
249
251
|
watch(normalizedPath, options, listener) {
|
|
@@ -251,9 +253,15 @@ export class NativeDesktopVaultAdapter {
|
|
|
251
253
|
const subscriptions = normalizedPath
|
|
252
254
|
.map((path) => this.watch(path, options, listener))
|
|
253
255
|
.filter((subscription) => Boolean(subscription));
|
|
256
|
+
const ready = Promise.all(subscriptions.map((subscription) => subscription.ready)).then(() => { });
|
|
257
|
+
void ready.catch(() => { });
|
|
254
258
|
return {
|
|
255
|
-
|
|
256
|
-
|
|
259
|
+
ready,
|
|
260
|
+
close: async () => {
|
|
261
|
+
const results = await Promise.allSettled(subscriptions.map(async (subscription) => subscription.close()));
|
|
262
|
+
const failed = results.find((result) => result.status === "rejected");
|
|
263
|
+
if (failed?.status === "rejected")
|
|
264
|
+
throw failed.reason;
|
|
257
265
|
},
|
|
258
266
|
};
|
|
259
267
|
}
|
|
@@ -284,6 +292,20 @@ export class NativeDesktopVaultAdapter {
|
|
|
284
292
|
});
|
|
285
293
|
return new Uint8Array(data).buffer;
|
|
286
294
|
}
|
|
295
|
+
async writeExclusive(normalizedPath, data, options = {}) {
|
|
296
|
+
const path = normalizeVaultPath(normalizedPath);
|
|
297
|
+
if (!hasNativeDesktopCapability("exclusive-create"))
|
|
298
|
+
throw createFsError("ENOTSUP", "Exclusive file creation is unavailable");
|
|
299
|
+
const bytes = typeof data === "string"
|
|
300
|
+
? new TextEncoder().encode(data)
|
|
301
|
+
: new Uint8Array(data);
|
|
302
|
+
await invokeNative("desktop_fs_create_exclusive", {
|
|
303
|
+
rootPath: this.rootPath,
|
|
304
|
+
normalizedPath: path,
|
|
305
|
+
data: [...bytes],
|
|
306
|
+
options,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
287
309
|
async openRead(normalizedPath) {
|
|
288
310
|
const path = normalizeVaultPath(normalizedPath);
|
|
289
311
|
const rootPath = this.rootPath;
|
|
@@ -335,7 +357,7 @@ export class NativeDesktopVaultAdapter {
|
|
|
335
357
|
try {
|
|
336
358
|
while (true) {
|
|
337
359
|
if (options.signal?.aborted) {
|
|
338
|
-
throw options.signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
360
|
+
throw (options.signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
339
361
|
}
|
|
340
362
|
const next = await reader.read();
|
|
341
363
|
if (next.done)
|
|
@@ -365,6 +387,19 @@ export class NativeDesktopVaultAdapter {
|
|
|
365
387
|
reader.releaseLock();
|
|
366
388
|
}
|
|
367
389
|
}
|
|
390
|
+
async writeTextIfUnchanged(normalizedPath, data, expected, options) {
|
|
391
|
+
if (expected !== null && typeof expected !== "string")
|
|
392
|
+
throw new Error("Invalid expected text snapshot");
|
|
393
|
+
if (!hasNativeDesktopCapability("conditional-text-write"))
|
|
394
|
+
throw new Error("Conditional text writes are unavailable in this native host");
|
|
395
|
+
await invokeNative("desktop_fs_write_text_if_unchanged", {
|
|
396
|
+
rootPath: this.rootPath,
|
|
397
|
+
normalizedPath: normalizeVaultPath(normalizedPath),
|
|
398
|
+
data,
|
|
399
|
+
expected,
|
|
400
|
+
options,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
368
403
|
async write(normalizedPath, data, options) {
|
|
369
404
|
await invokeNative("desktop_fs_write_text", {
|
|
370
405
|
rootPath: this.rootPath,
|
|
@@ -458,8 +493,26 @@ export class NativeDesktopVaultAdapter {
|
|
|
458
493
|
}
|
|
459
494
|
return this.getResourcePath(normalizedPath);
|
|
460
495
|
}
|
|
461
|
-
async trashSystem() {
|
|
462
|
-
|
|
496
|
+
async trashSystem(normalizedPath) {
|
|
497
|
+
const path = normalizeVaultPath(normalizedPath);
|
|
498
|
+
if (path === "/")
|
|
499
|
+
throw createFsError("EINVAL", "Cannot trash the vault root");
|
|
500
|
+
if (!hasNativeDesktopCapability("system-trash"))
|
|
501
|
+
return false;
|
|
502
|
+
try {
|
|
503
|
+
const handled = await invokeNative("desktop_fs_trash_system", {
|
|
504
|
+
rootPath: this.rootPath,
|
|
505
|
+
normalizedPath: path,
|
|
506
|
+
});
|
|
507
|
+
if (typeof handled !== "boolean")
|
|
508
|
+
throw createFsError("EINVAL", "Invalid native trash acknowledgement");
|
|
509
|
+
return handled;
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
if (error?.code === "ENOTSUP")
|
|
513
|
+
return false;
|
|
514
|
+
throw error;
|
|
515
|
+
}
|
|
463
516
|
}
|
|
464
517
|
async trashLocal(normalizedPath) {
|
|
465
518
|
const path = normalizeVaultPath(normalizedPath);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { EventDispatcher } from "../events";
|
|
2
|
+
import type { ChangeEvent, DataAdapter, NativeWatchSubscription, WatchErrorEvent, WatchOptions } from "./fs";
|
|
3
|
+
export declare class DirectoryWatcher extends EventDispatcher<{
|
|
4
|
+
error: [{
|
|
5
|
+
error: unknown;
|
|
6
|
+
path: string;
|
|
7
|
+
}];
|
|
8
|
+
all: [event: ChangeEvent | WatchErrorEvent];
|
|
9
|
+
create: [path: string];
|
|
10
|
+
modify: [path: string];
|
|
11
|
+
delete: [path: string];
|
|
12
|
+
}> {
|
|
13
|
+
readonly adapter: DataAdapter;
|
|
14
|
+
readonly pollingInterval: number;
|
|
15
|
+
private watchers;
|
|
16
|
+
private retired;
|
|
17
|
+
constructor(adapter: DataAdapter, pollingInterval?: number);
|
|
18
|
+
watch(path: string | string[], options?: WatchOptions): NativeWatchSubscription;
|
|
19
|
+
unwatch(path: string): Promise<void>;
|
|
20
|
+
close(): Promise<void>;
|
|
21
|
+
private live;
|
|
22
|
+
private retire;
|
|
23
|
+
private deliver;
|
|
24
|
+
private eventPath;
|
|
25
|
+
private snapshot;
|
|
26
|
+
private poll;
|
|
27
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { EventDispatcher } from "../events";
|
|
2
|
+
import { joinPath, normalizePath } from "./path";
|
|
3
|
+
async function drain(operations) {
|
|
4
|
+
const results = await Promise.allSettled(operations);
|
|
5
|
+
const failed = results.find((result) => result.status === "rejected");
|
|
6
|
+
if (failed?.status === "rejected")
|
|
7
|
+
throw failed.reason;
|
|
8
|
+
}
|
|
9
|
+
export class DirectoryWatcher extends EventDispatcher {
|
|
10
|
+
adapter;
|
|
11
|
+
pollingInterval;
|
|
12
|
+
watchers = new Map();
|
|
13
|
+
retired = new Set();
|
|
14
|
+
constructor(adapter, pollingInterval = 5000) {
|
|
15
|
+
super();
|
|
16
|
+
this.adapter = adapter;
|
|
17
|
+
this.pollingInterval = pollingInterval;
|
|
18
|
+
}
|
|
19
|
+
watch(path, options = {}) {
|
|
20
|
+
if (Array.isArray(path)) {
|
|
21
|
+
const subscriptions = path.map((entry) => this.watch(entry, options));
|
|
22
|
+
const ready = Promise.all(subscriptions.map((entry) => entry.ready)).then(() => { });
|
|
23
|
+
void ready.catch(() => { });
|
|
24
|
+
return {
|
|
25
|
+
ready,
|
|
26
|
+
close: () => drain(subscriptions.map(async (entry) => entry.close())),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
path = normalizePath(path);
|
|
30
|
+
const previous = this.watchers.get(path);
|
|
31
|
+
if (previous)
|
|
32
|
+
void this.retire(previous).catch(() => { });
|
|
33
|
+
const state = {
|
|
34
|
+
path,
|
|
35
|
+
recursive: options.recursive ?? false,
|
|
36
|
+
interval: options.interval ?? this.pollingInterval,
|
|
37
|
+
active: true,
|
|
38
|
+
registering: false,
|
|
39
|
+
snapshot: null,
|
|
40
|
+
timeout: null,
|
|
41
|
+
native: null,
|
|
42
|
+
pending: Promise.resolve(),
|
|
43
|
+
};
|
|
44
|
+
this.watchers.set(path, state);
|
|
45
|
+
let ready = Promise.resolve();
|
|
46
|
+
const adapter = this.adapter;
|
|
47
|
+
if (typeof adapter.watch === "function" &&
|
|
48
|
+
adapter.getCapabilities?.().nativeWatch === true) {
|
|
49
|
+
state.registering = true;
|
|
50
|
+
try {
|
|
51
|
+
state.native =
|
|
52
|
+
adapter.watch(path, options, (event) => {
|
|
53
|
+
if (!this.live(state))
|
|
54
|
+
return;
|
|
55
|
+
this.deliver(event.type === "error"
|
|
56
|
+
? { ...event, path: this.eventPath(event.path) }
|
|
57
|
+
: {
|
|
58
|
+
...event,
|
|
59
|
+
path: this.eventPath(event.path),
|
|
60
|
+
...(event.previousPath === undefined
|
|
61
|
+
? {}
|
|
62
|
+
: { previousPath: this.eventPath(event.previousPath) }),
|
|
63
|
+
});
|
|
64
|
+
}) ?? null;
|
|
65
|
+
ready = state.native?.ready ?? ready;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
state.active = false;
|
|
69
|
+
if (this.watchers.get(path) === state)
|
|
70
|
+
this.watchers.delete(path);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
state.registering = false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
state.pending = this.poll(state);
|
|
79
|
+
ready = state.pending;
|
|
80
|
+
}
|
|
81
|
+
void ready.catch(() => { });
|
|
82
|
+
return { ready, close: () => this.retire(state) };
|
|
83
|
+
}
|
|
84
|
+
unwatch(path) {
|
|
85
|
+
path = normalizePath(path);
|
|
86
|
+
return drain([...new Set([...this.watchers.values(), ...this.retired])]
|
|
87
|
+
.filter((state) => state.path === path)
|
|
88
|
+
.map((state) => this.retire(state)));
|
|
89
|
+
}
|
|
90
|
+
close() {
|
|
91
|
+
return drain([...new Set([...this.watchers.values(), ...this.retired])].map((state) => this.retire(state)));
|
|
92
|
+
}
|
|
93
|
+
live(state) {
|
|
94
|
+
return state.active && this.watchers.get(state.path) === state;
|
|
95
|
+
}
|
|
96
|
+
retire(state) {
|
|
97
|
+
state.active = false;
|
|
98
|
+
if (this.watchers.get(state.path) === state)
|
|
99
|
+
this.watchers.delete(state.path);
|
|
100
|
+
if (state.timeout)
|
|
101
|
+
clearTimeout(state.timeout);
|
|
102
|
+
state.timeout = null;
|
|
103
|
+
if (state.closing)
|
|
104
|
+
return state.closing;
|
|
105
|
+
this.retired.add(state);
|
|
106
|
+
let cleanup;
|
|
107
|
+
try {
|
|
108
|
+
cleanup = state.registering
|
|
109
|
+
? Promise.resolve().then(() => state.native?.close())
|
|
110
|
+
: state.native?.close();
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
cleanup = Promise.reject(error);
|
|
114
|
+
}
|
|
115
|
+
state.closing = drain([state.pending, cleanup]).then(() => {
|
|
116
|
+
this.retired.delete(state);
|
|
117
|
+
});
|
|
118
|
+
void state.closing.catch(() => {
|
|
119
|
+
state.closing = undefined;
|
|
120
|
+
});
|
|
121
|
+
return state.closing;
|
|
122
|
+
}
|
|
123
|
+
deliver(event) {
|
|
124
|
+
this.dispatch("all", event);
|
|
125
|
+
if (event.type === "error")
|
|
126
|
+
this.dispatch("error", { error: event.error, path: event.path });
|
|
127
|
+
else
|
|
128
|
+
this.dispatch(event.type, event.path);
|
|
129
|
+
}
|
|
130
|
+
eventPath(path) {
|
|
131
|
+
const normalized = normalizePath(path);
|
|
132
|
+
return normalized === "/" ? "/" : normalized.replace(/^\/+/, "");
|
|
133
|
+
}
|
|
134
|
+
async snapshot(path, recursive) {
|
|
135
|
+
const snapshot = new Map();
|
|
136
|
+
const stats = await this.adapter.stat(path);
|
|
137
|
+
if (!stats)
|
|
138
|
+
return snapshot;
|
|
139
|
+
snapshot.set(this.eventPath(path), stats);
|
|
140
|
+
if (stats.type !== "folder")
|
|
141
|
+
return snapshot;
|
|
142
|
+
const { files, folders } = await this.adapter.list(path);
|
|
143
|
+
for (const file of files) {
|
|
144
|
+
const entry = joinPath(path, file);
|
|
145
|
+
const stat = await this.adapter.stat(entry);
|
|
146
|
+
if (stat)
|
|
147
|
+
snapshot.set(this.eventPath(entry), stat);
|
|
148
|
+
}
|
|
149
|
+
for (const folder of folders) {
|
|
150
|
+
const entry = joinPath(path, folder);
|
|
151
|
+
if (recursive) {
|
|
152
|
+
for (const [key, stat] of await this.snapshot(entry, true))
|
|
153
|
+
snapshot.set(key, stat);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
const stat = await this.adapter.stat(entry);
|
|
157
|
+
if (stat)
|
|
158
|
+
snapshot.set(this.eventPath(entry), stat);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return snapshot;
|
|
162
|
+
}
|
|
163
|
+
async poll(state) {
|
|
164
|
+
try {
|
|
165
|
+
const next = await this.snapshot(state.path, state.recursive);
|
|
166
|
+
if (!this.live(state))
|
|
167
|
+
return;
|
|
168
|
+
if (state.snapshot) {
|
|
169
|
+
const changes = [];
|
|
170
|
+
for (const [path, stat] of next) {
|
|
171
|
+
const previous = state.snapshot.get(path);
|
|
172
|
+
if (!previous)
|
|
173
|
+
changes.push({ type: "create", path });
|
|
174
|
+
else if (previous.mtime !== stat.mtime ||
|
|
175
|
+
previous.size !== stat.size ||
|
|
176
|
+
previous.type !== stat.type)
|
|
177
|
+
changes.push({ type: "modify", path });
|
|
178
|
+
}
|
|
179
|
+
for (const path of state.snapshot.keys())
|
|
180
|
+
if (!next.has(path))
|
|
181
|
+
changes.push({ type: "delete", path });
|
|
182
|
+
for (const event of changes) {
|
|
183
|
+
if (!this.live(state))
|
|
184
|
+
break;
|
|
185
|
+
this.deliver(event);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
state.snapshot = next;
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (this.live(state))
|
|
192
|
+
this.deliver({ type: "error", path: state.path, error });
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
if (this.live(state))
|
|
196
|
+
state.timeout = setTimeout(() => {
|
|
197
|
+
state.pending = this.poll(state);
|
|
198
|
+
}, state.interval);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|