@lapis-notes/bases 0.1.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 +6 -0
- package/LICENSE.md +8 -0
- package/README.md +20 -0
- package/dist/bases-plugin.d.ts +10 -0
- package/dist/bases-plugin.js +118 -0
- package/dist/bases-view/app-database-query-source.d.ts +30 -0
- package/dist/bases-view/app-database-query-source.js +248 -0
- package/dist/bases-view/bases.svelte.d.ts +569 -0
- package/dist/bases-view/bases.svelte.js +910 -0
- package/dist/bases-view/card-view/components/card-placeholder.svelte +68 -0
- package/dist/bases-view/card-view/components/card-placeholder.svelte.d.ts +7 -0
- package/dist/bases-view/card-view/components/card.svelte +289 -0
- package/dist/bases-view/card-view/components/card.svelte.d.ts +7 -0
- package/dist/bases-view/card-view/index.d.ts +11 -0
- package/dist/bases-view/card-view/index.js +55 -0
- package/dist/bases-view/cell-editor-options.d.ts +2 -0
- package/dist/bases-view/cell-editor-options.js +26 -0
- package/dist/bases-view/cell-editor.svelte +206 -0
- package/dist/bases-view/cell-editor.svelte.d.ts +12 -0
- package/dist/bases-view/cell.svelte +132 -0
- package/dist/bases-view/cell.svelte.d.ts +16 -0
- package/dist/bases-view/column-definition.d.ts +22 -0
- package/dist/bases-view/column-definition.js +149 -0
- package/dist/bases-view/columns.d.ts +15 -0
- package/dist/bases-view/columns.js +103 -0
- package/dist/bases-view/components/Tag.svelte +14 -0
- package/dist/bases-view/components/Tag.svelte.d.ts +8 -0
- package/dist/bases-view/components/autocomplete.svelte +45 -0
- package/dist/bases-view/components/autocomplete.svelte.d.ts +17 -0
- package/dist/bases-view/components/card-image.svelte +121 -0
- package/dist/bases-view/components/card-image.svelte.d.ts +11 -0
- package/dist/bases-view/components/column-visibility.svelte +388 -0
- package/dist/bases-view/components/column-visibility.svelte.d.ts +13 -0
- package/dist/bases-view/components/datetime.svelte +35 -0
- package/dist/bases-view/components/datetime.svelte.d.ts +7 -0
- package/dist/bases-view/components/file.svelte +31 -0
- package/dist/bases-view/components/file.svelte.d.ts +8 -0
- package/dist/bases-view/components/filters/filter-editor.svelte +102 -0
- package/dist/bases-view/components/filters/filter-editor.svelte.d.ts +10 -0
- package/dist/bases-view/components/filters/filter-group.svelte +142 -0
- package/dist/bases-view/components/filters/filter-group.svelte.d.ts +11 -0
- package/dist/bases-view/components/filters/filter-properties.svelte +77 -0
- package/dist/bases-view/components/filters/filter-properties.svelte.d.ts +8 -0
- package/dist/bases-view/components/filters/filter-root.svelte +112 -0
- package/dist/bases-view/components/filters/filter-root.svelte.d.ts +12 -0
- package/dist/bases-view/components/filters/filter-row.svelte +351 -0
- package/dist/bases-view/components/filters/filter-row.svelte.d.ts +11 -0
- package/dist/bases-view/components/group/group-root.svelte +120 -0
- package/dist/bases-view/components/group/group-root.svelte.d.ts +8 -0
- package/dist/bases-view/components/limit.svelte +100 -0
- package/dist/bases-view/components/limit.svelte.d.ts +12 -0
- package/dist/bases-view/components/query-editor.svelte +230 -0
- package/dist/bases-view/components/query-editor.svelte.d.ts +15 -0
- package/dist/bases-view/components/sort/sort-direction-labels.d.ts +9 -0
- package/dist/bases-view/components/sort/sort-direction-labels.js +25 -0
- package/dist/bases-view/components/sort/sort-root.svelte +239 -0
- package/dist/bases-view/components/sort/sort-root.svelte.d.ts +11 -0
- package/dist/bases-view/components/sort/sort-row.svelte +190 -0
- package/dist/bases-view/components/sort/sort-row.svelte.d.ts +15 -0
- package/dist/bases-view/components/summary-strip.svelte +56 -0
- package/dist/bases-view/components/summary-strip.svelte.d.ts +8 -0
- package/dist/bases-view/components/tag-chip.svelte +47 -0
- package/dist/bases-view/components/tag-chip.svelte.d.ts +12 -0
- package/dist/bases-view/components/views.svelte +178 -0
- package/dist/bases-view/components/views.svelte.d.ts +12 -0
- package/dist/bases-view/data-table-adapter/data-table.svelte.d.ts +45 -0
- package/dist/bases-view/data-table-adapter/data-table.svelte.js +112 -0
- package/dist/bases-view/data-table-adapter/flex-render.svelte +43 -0
- package/dist/bases-view/data-table-adapter/flex-render.svelte.d.ts +30 -0
- package/dist/bases-view/data-table-adapter/index.d.ts +3 -0
- package/dist/bases-view/data-table-adapter/index.js +3 -0
- package/dist/bases-view/data-table-adapter/render-helpers.d.ts +101 -0
- package/dist/bases-view/data-table-adapter/render-helpers.js +110 -0
- package/dist/bases-view/db.d.ts +31 -0
- package/dist/bases-view/db.js +174 -0
- package/dist/bases-view/document-core.d.ts +2 -0
- package/dist/bases-view/document-core.js +166 -0
- package/dist/bases-view/export-core.d.ts +4 -0
- package/dist/bases-view/export-core.js +31 -0
- package/dist/bases-view/file-fields-core.d.ts +31 -0
- package/dist/bases-view/file-fields-core.js +114 -0
- package/dist/bases-view/filter-parser.d.ts +27 -0
- package/dist/bases-view/filter-parser.js +315 -0
- package/dist/bases-view/functions-core.d.ts +66 -0
- package/dist/bases-view/functions-core.js +265 -0
- package/dist/bases-view/functions.d.ts +5 -0
- package/dist/bases-view/functions.js +107 -0
- package/dist/bases-view/fuzzy-search.d.ts +6 -0
- package/dist/bases-view/fuzzy-search.js +15 -0
- package/dist/bases-view/hooks/useResizeObserver.svelte.d.ts +7 -0
- package/dist/bases-view/hooks/useResizeObserver.svelte.js +26 -0
- package/dist/bases-view/index.d.ts +66 -0
- package/dist/bases-view/index.js +231 -0
- package/dist/bases-view/list-view/index.d.ts +10 -0
- package/dist/bases-view/list-view/index.js +17 -0
- package/dist/bases-view/list-view/list.svelte +156 -0
- package/dist/bases-view/list-view/list.svelte.d.ts +7 -0
- package/dist/bases-view/map-view/index.d.ts +10 -0
- package/dist/bases-view/map-view/index.js +17 -0
- package/dist/bases-view/map-view/view.svelte +28 -0
- package/dist/bases-view/map-view/view.svelte.d.ts +7 -0
- package/dist/bases-view/metadata-invalidation-core.d.ts +34 -0
- package/dist/bases-view/metadata-invalidation-core.js +162 -0
- package/dist/bases-view/metadata-type-info.d.ts +7 -0
- package/dist/bases-view/metadata-type-info.js +10 -0
- package/dist/bases-view/models.d.ts +90 -0
- package/dist/bases-view/models.js +133 -0
- package/dist/bases-view/query-result-core.d.ts +8 -0
- package/dist/bases-view/query-result-core.js +16 -0
- package/dist/bases-view/resource-url.d.ts +2 -0
- package/dist/bases-view/resource-url.js +21 -0
- package/dist/bases-view/search-core.d.ts +9 -0
- package/dist/bases-view/search-core.js +20 -0
- package/dist/bases-view/summary-core.d.ts +7 -0
- package/dist/bases-view/summary-core.js +17 -0
- package/dist/bases-view/table-view/components/div-table.svelte +464 -0
- package/dist/bases-view/table-view/components/div-table.svelte.d.ts +32 -0
- package/dist/bases-view/table-view/components/dnd-style.d.ts +3 -0
- package/dist/bases-view/table-view/components/dnd-style.js +6 -0
- package/dist/bases-view/table-view/components/sort-header.svelte +48 -0
- package/dist/bases-view/table-view/components/sort-header.svelte.d.ts +11 -0
- package/dist/bases-view/table-view/components/table-column-tracks.d.ts +14 -0
- package/dist/bases-view/table-view/components/table-column-tracks.js +27 -0
- package/dist/bases-view/table-view/components/table-header.svelte +146 -0
- package/dist/bases-view/table-view/components/table-header.svelte.d.ts +10 -0
- package/dist/bases-view/table-view/components/table-placeholder.svelte +46 -0
- package/dist/bases-view/table-view/components/table-placeholder.svelte.d.ts +30 -0
- package/dist/bases-view/table-view/components/table-virtualizer-core.d.ts +13 -0
- package/dist/bases-view/table-view/components/table-virtualizer-core.js +12 -0
- package/dist/bases-view/table-view/components/table.svelte +997 -0
- package/dist/bases-view/table-view/components/table.svelte.d.ts +32 -0
- package/dist/bases-view/table-view/index.svelte.d.ts +10 -0
- package/dist/bases-view/table-view/index.svelte.js +30 -0
- package/dist/bases-view/unknown-view/index.d.ts +10 -0
- package/dist/bases-view/unknown-view/index.js +19 -0
- package/dist/bases-view/unknown-view/view.svelte +11 -0
- package/dist/bases-view/unknown-view/view.svelte.d.ts +6 -0
- package/dist/bases-view/view-header.svelte +142 -0
- package/dist/bases-view/view-header.svelte.d.ts +28 -0
- package/dist/bases-view/view-registration-inventory.d.ts +22 -0
- package/dist/bases-view/view-registration-inventory.js +7 -0
- package/dist/bases-view/view-settings.svelte +350 -0
- package/dist/bases-view/view-settings.svelte.d.ts +9 -0
- package/dist/bases-view/view.svelte +192 -0
- package/dist/bases-view/view.svelte.d.ts +15 -0
- package/dist/bases-view-surface.svelte +31 -0
- package/dist/bases-view-surface.svelte.d.ts +14 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +8 -0
- package/dist/serialized-write-queue.d.ts +10 -0
- package/dist/serialized-write-queue.js +36 -0
- package/dist/styles.css +2164 -0
- package/manifest.json +33 -0
- package/package.json +90 -0
- package/styles.css +1 -0
package/CHANGELOG.md
ADDED
package/LICENSE.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# License
|
|
2
|
+
|
|
3
|
+
Copyright (c) LapisMD contributors.
|
|
4
|
+
|
|
5
|
+
This software is licensed under the GNU Affero General Public License, version
|
|
6
|
+
3 or (at your option) any later version (`AGPL-3.0-or-later`). The complete
|
|
7
|
+
license text is available from the
|
|
8
|
+
[GNU Project](https://www.gnu.org/licenses/agpl-3.0.html).
|
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Bases
|
|
2
|
+
|
|
3
|
+
Bases
|
|
4
|
+
|
|
5
|
+
## Install for static composition
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @lapis-notes/bases
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Register the exported plugin class in the application's `PluginProfile`. Import
|
|
12
|
+
`@lapis-notes/bases/styles.css?inline` and pass the CSS through the static
|
|
13
|
+
registration so the host owns its lifecycle.
|
|
14
|
+
|
|
15
|
+
The runtime plugin ID is `bases`. A matching signed
|
|
16
|
+
`bases-0.1.0.lapis-plugin` archive is attached to the
|
|
17
|
+
package-scoped GitHub release for manual or registry installation.
|
|
18
|
+
|
|
19
|
+
See the [repository README](https://github.com/lapismd/lapis-plugins#readme) for
|
|
20
|
+
development, validation, and release-gate details.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type App, Plugin, type PluginManifest } from "@lapis-notes/api";
|
|
2
|
+
export declare class BasesPlugin extends Plugin {
|
|
3
|
+
constructor(app: App, manifest?: PluginManifest);
|
|
4
|
+
onload(): Promise<void>;
|
|
5
|
+
private registerReadOnlyEmbedRenderer;
|
|
6
|
+
private registerReadOnlyCodeBlockRenderer;
|
|
7
|
+
private mountReadOnlyBases;
|
|
8
|
+
private renderError;
|
|
9
|
+
}
|
|
10
|
+
export default BasesPlugin;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { MarkdownRenderChild, Plugin, } from "@lapis-notes/api";
|
|
2
|
+
import { markupEditor } from "@lapis-notes/api/editor/core";
|
|
3
|
+
import { yaml } from "@codemirror/lang-yaml";
|
|
4
|
+
import { mount, unmount } from "svelte";
|
|
5
|
+
import BasesComponent from "./bases-view/view.svelte";
|
|
6
|
+
import { BasesView, BasesViewType, createBasesViewRegistrations, parseBasesDocument, } from "./bases-view";
|
|
7
|
+
import manifestSpec from "../../manifest.json";
|
|
8
|
+
export class BasesPlugin extends Plugin {
|
|
9
|
+
constructor(app, manifest = manifestSpec) {
|
|
10
|
+
super(app, manifest);
|
|
11
|
+
}
|
|
12
|
+
async onload() {
|
|
13
|
+
this.registerView(BasesViewType, (leaf) => new BasesView(leaf), {
|
|
14
|
+
kind: "file",
|
|
15
|
+
});
|
|
16
|
+
this.registerEditorView({
|
|
17
|
+
id: BasesViewType,
|
|
18
|
+
label: "Bases",
|
|
19
|
+
filenamePatterns: ["*.bases", "*.base"],
|
|
20
|
+
priority: "default",
|
|
21
|
+
});
|
|
22
|
+
this.registerExtensions(["bases", "base"], BasesViewType);
|
|
23
|
+
this.registerEditorExtension(() => [markupEditor({ language: "yaml", app: this.app }), yaml()], BasesViewType);
|
|
24
|
+
this.registerReadOnlyEmbedRenderer("base");
|
|
25
|
+
this.registerReadOnlyEmbedRenderer("bases");
|
|
26
|
+
this.registerReadOnlyCodeBlockRenderer("base");
|
|
27
|
+
this.registerReadOnlyCodeBlockRenderer("bases");
|
|
28
|
+
}
|
|
29
|
+
registerReadOnlyEmbedRenderer(extension) {
|
|
30
|
+
this.register(this.app.embedRegistry.register(extension, ({ containerEl, state }) => {
|
|
31
|
+
const file = state.file;
|
|
32
|
+
if (!file) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
let activeHandle = null;
|
|
36
|
+
let disposed = false;
|
|
37
|
+
void this.app.vault
|
|
38
|
+
.read(file)
|
|
39
|
+
.then((content) => {
|
|
40
|
+
if (disposed) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
activeHandle = this.mountReadOnlyBases(containerEl, content);
|
|
44
|
+
})
|
|
45
|
+
.catch((error) => {
|
|
46
|
+
if (disposed) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
this.renderError(containerEl, error instanceof Error
|
|
50
|
+
? `Unable to render this embedded base: ${error.message}`
|
|
51
|
+
: `Unable to render this embedded base: ${String(error)}`);
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
destroy: () => {
|
|
55
|
+
disposed = true;
|
|
56
|
+
activeHandle?.destroy();
|
|
57
|
+
activeHandle = null;
|
|
58
|
+
containerEl.replaceChildren();
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
registerReadOnlyCodeBlockRenderer(language) {
|
|
64
|
+
this.registerMarkdownCodeBlockProcessor(language, (source, el, ctx) => {
|
|
65
|
+
const handle = this.mountReadOnlyBases(el, source);
|
|
66
|
+
const child = new MarkdownRenderChild(el);
|
|
67
|
+
child.register(() => {
|
|
68
|
+
handle.destroy();
|
|
69
|
+
});
|
|
70
|
+
ctx.addChild(child);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
mountReadOnlyBases(containerEl, content) {
|
|
74
|
+
containerEl.replaceChildren();
|
|
75
|
+
let document;
|
|
76
|
+
try {
|
|
77
|
+
document = parseBasesDocument(content);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
this.renderError(containerEl, error instanceof Error
|
|
81
|
+
? `Unable to render this base: ${error.message}`
|
|
82
|
+
: `Unable to render this base: ${String(error)}`);
|
|
83
|
+
return {
|
|
84
|
+
destroy: () => {
|
|
85
|
+
containerEl.replaceChildren();
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const component = mount(BasesComponent, {
|
|
90
|
+
target: containerEl,
|
|
91
|
+
props: {
|
|
92
|
+
app: this.app,
|
|
93
|
+
document,
|
|
94
|
+
registrations: createBasesViewRegistrations(this.app),
|
|
95
|
+
onChange: () => { },
|
|
96
|
+
readOnly: true,
|
|
97
|
+
showHeader: false,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
destroy: () => {
|
|
102
|
+
void unmount(component);
|
|
103
|
+
containerEl.replaceChildren();
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
renderError(containerEl, message) {
|
|
108
|
+
containerEl.replaceChildren();
|
|
109
|
+
const wrapper = document.createElement("div");
|
|
110
|
+
wrapper.className = "bases-view-error";
|
|
111
|
+
const messageEl = document.createElement("p");
|
|
112
|
+
messageEl.className = "bases-view-error__message";
|
|
113
|
+
messageEl.textContent = message;
|
|
114
|
+
wrapper.appendChild(messageEl);
|
|
115
|
+
containerEl.appendChild(wrapper);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export default BasesPlugin;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type AppDatabase, type AppDatabaseIndexedMetadataQuery, type AppDatabaseIndexedMetadataRow, type CachedMetadata, type TFile } from "@lapis-notes/api";
|
|
2
|
+
import { type Filters, type SortColumn } from "./models";
|
|
3
|
+
type BuildBasesAppDatabaseQueryInput = {
|
|
4
|
+
documentFilter: Filters;
|
|
5
|
+
viewFilter?: Filters;
|
|
6
|
+
sort: SortColumn[];
|
|
7
|
+
limit: number | null | undefined;
|
|
8
|
+
};
|
|
9
|
+
type QuerySourceApp = {
|
|
10
|
+
vault: {
|
|
11
|
+
getFileByPath(path: string): TFile | null;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
type QuerySourceVaultRecord = {
|
|
15
|
+
id: string;
|
|
16
|
+
checksum?: string;
|
|
17
|
+
file: TFile;
|
|
18
|
+
cache: CachedMetadata | null;
|
|
19
|
+
backlinks?: string[];
|
|
20
|
+
};
|
|
21
|
+
export declare function buildBasesAppDatabaseQuery(input: BuildBasesAppDatabaseQueryInput): AppDatabaseIndexedMetadataQuery;
|
|
22
|
+
/**
|
|
23
|
+
* Read database candidates in bounded pages. When no pushed-down limit is
|
|
24
|
+
* available, final PEaQL sorting remains authoritative and candidates page by
|
|
25
|
+
* path so the cursor is stable across every provider.
|
|
26
|
+
*/
|
|
27
|
+
export declare function queryBasesAppDatabaseRows(database: Pick<AppDatabase, "queryIndexedMetadataPage">, query: AppDatabaseIndexedMetadataQuery, pageSize?: number): Promise<AppDatabaseIndexedMetadataRow[]>;
|
|
28
|
+
export declare function appDatabaseRowToVaultRecord(app: QuerySourceApp, row: AppDatabaseIndexedMetadataRow): QuerySourceVaultRecord;
|
|
29
|
+
export declare function appDatabaseRowsToVaultRecords(app: QuerySourceApp, rows: AppDatabaseIndexedMetadataRow[]): QuerySourceVaultRecord[];
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import {} from "@lapis-notes/api";
|
|
2
|
+
import { isFilterGroup, isFilterLine, } from "./models";
|
|
3
|
+
function clone(value) {
|
|
4
|
+
if (value === undefined || value === null) {
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
return JSON.parse(JSON.stringify(value));
|
|
8
|
+
}
|
|
9
|
+
function createSyntheticVaultFile(row) {
|
|
10
|
+
const pathSegments = row.file.path.split("/");
|
|
11
|
+
const name = pathSegments.at(-1) ?? row.file.path;
|
|
12
|
+
const extension = row.file.extension;
|
|
13
|
+
const suffix = extension ? `.${extension}` : "";
|
|
14
|
+
const baseName = suffix && name.endsWith(suffix) ? name.slice(0, -suffix.length) : name;
|
|
15
|
+
return {
|
|
16
|
+
path: row.file.path,
|
|
17
|
+
name,
|
|
18
|
+
baseName,
|
|
19
|
+
extension,
|
|
20
|
+
parent: null,
|
|
21
|
+
stat: {
|
|
22
|
+
ctime: row.file.mtime,
|
|
23
|
+
mtime: row.file.mtime,
|
|
24
|
+
size: row.file.size,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function appendUnique(target, value) {
|
|
29
|
+
if (!value || target.includes(value)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
target.push(value);
|
|
33
|
+
}
|
|
34
|
+
function appendPropertyFilter(target, filter) {
|
|
35
|
+
if (target.some((existing) => existing.name === filter.name &&
|
|
36
|
+
existing.op === filter.op &&
|
|
37
|
+
existing.value === filter.value)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
target.push(filter);
|
|
41
|
+
}
|
|
42
|
+
function collectConjunctiveFilterLines(filter) {
|
|
43
|
+
if (!filter || typeof filter === "string") {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
if (isFilterLine(filter)) {
|
|
47
|
+
return [filter];
|
|
48
|
+
}
|
|
49
|
+
if (!isFilterGroup(filter) || !("and" in filter)) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
return filter.and.flatMap((entry) => collectConjunctiveFilterLines(entry));
|
|
53
|
+
}
|
|
54
|
+
function normalizePropertyName(column) {
|
|
55
|
+
if (!column || column === "file" || column.startsWith("formula.")) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (column.startsWith("file.")) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return column.startsWith("note.") ? column.slice("note.".length) : column;
|
|
62
|
+
}
|
|
63
|
+
function isScalarFilterValue(value) {
|
|
64
|
+
return (value === null ||
|
|
65
|
+
typeof value === "string" ||
|
|
66
|
+
typeof value === "number" ||
|
|
67
|
+
typeof value === "boolean");
|
|
68
|
+
}
|
|
69
|
+
function lowerFilterLine(query, filter) {
|
|
70
|
+
if (filter.custom) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const rawValues = (Array.isArray(filter.value) ? filter.value : [filter.value]);
|
|
74
|
+
const values = rawValues.filter(isScalarFilterValue);
|
|
75
|
+
if (filter.column === "file" && filter.op === "inFolder") {
|
|
76
|
+
for (const value of values) {
|
|
77
|
+
if (typeof value === "string") {
|
|
78
|
+
appendUnique((query.pathPrefixes ??= []), value);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (filter.column === "file" && filter.op === "hasTag") {
|
|
84
|
+
for (const value of values) {
|
|
85
|
+
if (typeof value === "string") {
|
|
86
|
+
appendUnique((query.requiredTags ??= []), value);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (filter.column === "file" && filter.op === "hasProperty") {
|
|
92
|
+
for (const value of values) {
|
|
93
|
+
if (typeof value === "string") {
|
|
94
|
+
appendPropertyFilter((query.propertyFilters ??= []), {
|
|
95
|
+
name: value,
|
|
96
|
+
op: "exists",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (filter.column === "file" && filter.op === "!hasProperty") {
|
|
103
|
+
for (const value of values) {
|
|
104
|
+
if (typeof value === "string") {
|
|
105
|
+
appendPropertyFilter((query.propertyFilters ??= []), {
|
|
106
|
+
name: value,
|
|
107
|
+
op: "not-exists",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (filter.column === "file.ext" && filter.op === "=") {
|
|
114
|
+
for (const value of values) {
|
|
115
|
+
if (typeof value === "string") {
|
|
116
|
+
appendUnique((query.extensions ??= []), value);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const propertyName = normalizePropertyName(filter.column);
|
|
122
|
+
if (!propertyName) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (["=", "!=", ">", ">=", "<", "<="].includes(filter.op)) {
|
|
126
|
+
const value = values[0];
|
|
127
|
+
if (value !== undefined) {
|
|
128
|
+
appendPropertyFilter((query.propertyFilters ??= []), {
|
|
129
|
+
name: propertyName,
|
|
130
|
+
op: filter.op,
|
|
131
|
+
value,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function lowerSort(sort) {
|
|
137
|
+
if (sort.property === "file.path") {
|
|
138
|
+
return {
|
|
139
|
+
field: { kind: "file", field: "path" },
|
|
140
|
+
direction: sort.direction,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (sort.property === "file.ext") {
|
|
144
|
+
return {
|
|
145
|
+
field: { kind: "file", field: "extension" },
|
|
146
|
+
direction: sort.direction,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (sort.property === "file.mtime") {
|
|
150
|
+
return {
|
|
151
|
+
field: { kind: "file", field: "mtime" },
|
|
152
|
+
direction: sort.direction,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (sort.property === "file.size") {
|
|
156
|
+
return {
|
|
157
|
+
field: { kind: "file", field: "size" },
|
|
158
|
+
direction: sort.direction,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const propertyName = normalizePropertyName(sort.property);
|
|
162
|
+
if (!propertyName) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
field: { kind: "property", name: propertyName },
|
|
167
|
+
direction: sort.direction,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
export function buildBasesAppDatabaseQuery(input) {
|
|
171
|
+
const query = {
|
|
172
|
+
excludeHiddenPaths: true,
|
|
173
|
+
};
|
|
174
|
+
for (const filter of collectConjunctiveFilterLines(input.documentFilter)) {
|
|
175
|
+
lowerFilterLine(query, filter);
|
|
176
|
+
}
|
|
177
|
+
for (const filter of collectConjunctiveFilterLines(input.viewFilter)) {
|
|
178
|
+
lowerFilterLine(query, filter);
|
|
179
|
+
}
|
|
180
|
+
const loweredSorts = input.sort
|
|
181
|
+
.map(lowerSort)
|
|
182
|
+
.filter((sort) => sort !== null);
|
|
183
|
+
if (loweredSorts.length > 0 && loweredSorts.length === input.sort.length) {
|
|
184
|
+
query.sort = loweredSorts;
|
|
185
|
+
if (input.limit && input.limit > 0) {
|
|
186
|
+
query.limit = input.limit;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return query;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Read database candidates in bounded pages. When no pushed-down limit is
|
|
193
|
+
* available, final PEaQL sorting remains authoritative and candidates page by
|
|
194
|
+
* path so the cursor is stable across every provider.
|
|
195
|
+
*/
|
|
196
|
+
export async function queryBasesAppDatabaseRows(database, query, pageSize = 500) {
|
|
197
|
+
if (query.limit && query.limit > 0) {
|
|
198
|
+
return (await database.queryIndexedMetadataPage({
|
|
199
|
+
query,
|
|
200
|
+
limit: query.limit,
|
|
201
|
+
})).rows;
|
|
202
|
+
}
|
|
203
|
+
const rows = [];
|
|
204
|
+
const pagedQuery = { ...query, sort: undefined, limit: undefined };
|
|
205
|
+
let after;
|
|
206
|
+
do {
|
|
207
|
+
const page = await database.queryIndexedMetadataPage({
|
|
208
|
+
query: pagedQuery,
|
|
209
|
+
after,
|
|
210
|
+
limit: pageSize,
|
|
211
|
+
});
|
|
212
|
+
rows.push(...page.rows);
|
|
213
|
+
after = page.nextCursor;
|
|
214
|
+
} while (after);
|
|
215
|
+
return rows;
|
|
216
|
+
}
|
|
217
|
+
export function appDatabaseRowToVaultRecord(app, row) {
|
|
218
|
+
const cache = clone((row.metadata?.metadata ?? {})) ?? {};
|
|
219
|
+
const frontmatter = { ...(cache.frontmatter ?? {}) };
|
|
220
|
+
for (const property of row.properties) {
|
|
221
|
+
if (!(property.name in frontmatter)) {
|
|
222
|
+
frontmatter[property.name] = property.value;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
cache.frontmatter = frontmatter;
|
|
226
|
+
if ((cache.tags?.length ?? 0) === 0 && row.tags.length > 0) {
|
|
227
|
+
cache.tags = row.tags.map((tag) => ({ tag: tag.tag }));
|
|
228
|
+
}
|
|
229
|
+
if ((cache.links?.length ?? 0) === 0) {
|
|
230
|
+
cache.links = row.links
|
|
231
|
+
.filter((link) => link.type === "link")
|
|
232
|
+
.map((link) => ({ link: link.resolvedTargetPath ?? link.targetText }));
|
|
233
|
+
}
|
|
234
|
+
if ((cache.embeds?.length ?? 0) === 0) {
|
|
235
|
+
cache.embeds = row.links
|
|
236
|
+
.filter((link) => link.type === "embed")
|
|
237
|
+
.map((link) => ({ link: link.resolvedTargetPath ?? link.targetText }));
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
id: row.file.path,
|
|
241
|
+
checksum: row.file.hash,
|
|
242
|
+
file: app.vault.getFileByPath(row.file.path) ?? createSyntheticVaultFile(row),
|
|
243
|
+
cache,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
export function appDatabaseRowsToVaultRecords(app, rows) {
|
|
247
|
+
return rows.map((row) => appDatabaseRowToVaultRecord(app, row));
|
|
248
|
+
}
|