@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.
Files changed (155) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE.md +8 -0
  3. package/README.md +20 -0
  4. package/dist/bases-plugin.d.ts +10 -0
  5. package/dist/bases-plugin.js +118 -0
  6. package/dist/bases-view/app-database-query-source.d.ts +30 -0
  7. package/dist/bases-view/app-database-query-source.js +248 -0
  8. package/dist/bases-view/bases.svelte.d.ts +569 -0
  9. package/dist/bases-view/bases.svelte.js +910 -0
  10. package/dist/bases-view/card-view/components/card-placeholder.svelte +68 -0
  11. package/dist/bases-view/card-view/components/card-placeholder.svelte.d.ts +7 -0
  12. package/dist/bases-view/card-view/components/card.svelte +289 -0
  13. package/dist/bases-view/card-view/components/card.svelte.d.ts +7 -0
  14. package/dist/bases-view/card-view/index.d.ts +11 -0
  15. package/dist/bases-view/card-view/index.js +55 -0
  16. package/dist/bases-view/cell-editor-options.d.ts +2 -0
  17. package/dist/bases-view/cell-editor-options.js +26 -0
  18. package/dist/bases-view/cell-editor.svelte +206 -0
  19. package/dist/bases-view/cell-editor.svelte.d.ts +12 -0
  20. package/dist/bases-view/cell.svelte +132 -0
  21. package/dist/bases-view/cell.svelte.d.ts +16 -0
  22. package/dist/bases-view/column-definition.d.ts +22 -0
  23. package/dist/bases-view/column-definition.js +149 -0
  24. package/dist/bases-view/columns.d.ts +15 -0
  25. package/dist/bases-view/columns.js +103 -0
  26. package/dist/bases-view/components/Tag.svelte +14 -0
  27. package/dist/bases-view/components/Tag.svelte.d.ts +8 -0
  28. package/dist/bases-view/components/autocomplete.svelte +45 -0
  29. package/dist/bases-view/components/autocomplete.svelte.d.ts +17 -0
  30. package/dist/bases-view/components/card-image.svelte +121 -0
  31. package/dist/bases-view/components/card-image.svelte.d.ts +11 -0
  32. package/dist/bases-view/components/column-visibility.svelte +388 -0
  33. package/dist/bases-view/components/column-visibility.svelte.d.ts +13 -0
  34. package/dist/bases-view/components/datetime.svelte +35 -0
  35. package/dist/bases-view/components/datetime.svelte.d.ts +7 -0
  36. package/dist/bases-view/components/file.svelte +31 -0
  37. package/dist/bases-view/components/file.svelte.d.ts +8 -0
  38. package/dist/bases-view/components/filters/filter-editor.svelte +102 -0
  39. package/dist/bases-view/components/filters/filter-editor.svelte.d.ts +10 -0
  40. package/dist/bases-view/components/filters/filter-group.svelte +142 -0
  41. package/dist/bases-view/components/filters/filter-group.svelte.d.ts +11 -0
  42. package/dist/bases-view/components/filters/filter-properties.svelte +77 -0
  43. package/dist/bases-view/components/filters/filter-properties.svelte.d.ts +8 -0
  44. package/dist/bases-view/components/filters/filter-root.svelte +112 -0
  45. package/dist/bases-view/components/filters/filter-root.svelte.d.ts +12 -0
  46. package/dist/bases-view/components/filters/filter-row.svelte +351 -0
  47. package/dist/bases-view/components/filters/filter-row.svelte.d.ts +11 -0
  48. package/dist/bases-view/components/group/group-root.svelte +120 -0
  49. package/dist/bases-view/components/group/group-root.svelte.d.ts +8 -0
  50. package/dist/bases-view/components/limit.svelte +100 -0
  51. package/dist/bases-view/components/limit.svelte.d.ts +12 -0
  52. package/dist/bases-view/components/query-editor.svelte +230 -0
  53. package/dist/bases-view/components/query-editor.svelte.d.ts +15 -0
  54. package/dist/bases-view/components/sort/sort-direction-labels.d.ts +9 -0
  55. package/dist/bases-view/components/sort/sort-direction-labels.js +25 -0
  56. package/dist/bases-view/components/sort/sort-root.svelte +239 -0
  57. package/dist/bases-view/components/sort/sort-root.svelte.d.ts +11 -0
  58. package/dist/bases-view/components/sort/sort-row.svelte +190 -0
  59. package/dist/bases-view/components/sort/sort-row.svelte.d.ts +15 -0
  60. package/dist/bases-view/components/summary-strip.svelte +56 -0
  61. package/dist/bases-view/components/summary-strip.svelte.d.ts +8 -0
  62. package/dist/bases-view/components/tag-chip.svelte +47 -0
  63. package/dist/bases-view/components/tag-chip.svelte.d.ts +12 -0
  64. package/dist/bases-view/components/views.svelte +178 -0
  65. package/dist/bases-view/components/views.svelte.d.ts +12 -0
  66. package/dist/bases-view/data-table-adapter/data-table.svelte.d.ts +45 -0
  67. package/dist/bases-view/data-table-adapter/data-table.svelte.js +112 -0
  68. package/dist/bases-view/data-table-adapter/flex-render.svelte +43 -0
  69. package/dist/bases-view/data-table-adapter/flex-render.svelte.d.ts +30 -0
  70. package/dist/bases-view/data-table-adapter/index.d.ts +3 -0
  71. package/dist/bases-view/data-table-adapter/index.js +3 -0
  72. package/dist/bases-view/data-table-adapter/render-helpers.d.ts +101 -0
  73. package/dist/bases-view/data-table-adapter/render-helpers.js +110 -0
  74. package/dist/bases-view/db.d.ts +31 -0
  75. package/dist/bases-view/db.js +174 -0
  76. package/dist/bases-view/document-core.d.ts +2 -0
  77. package/dist/bases-view/document-core.js +166 -0
  78. package/dist/bases-view/export-core.d.ts +4 -0
  79. package/dist/bases-view/export-core.js +31 -0
  80. package/dist/bases-view/file-fields-core.d.ts +31 -0
  81. package/dist/bases-view/file-fields-core.js +114 -0
  82. package/dist/bases-view/filter-parser.d.ts +27 -0
  83. package/dist/bases-view/filter-parser.js +315 -0
  84. package/dist/bases-view/functions-core.d.ts +66 -0
  85. package/dist/bases-view/functions-core.js +265 -0
  86. package/dist/bases-view/functions.d.ts +5 -0
  87. package/dist/bases-view/functions.js +107 -0
  88. package/dist/bases-view/fuzzy-search.d.ts +6 -0
  89. package/dist/bases-view/fuzzy-search.js +15 -0
  90. package/dist/bases-view/hooks/useResizeObserver.svelte.d.ts +7 -0
  91. package/dist/bases-view/hooks/useResizeObserver.svelte.js +26 -0
  92. package/dist/bases-view/index.d.ts +66 -0
  93. package/dist/bases-view/index.js +231 -0
  94. package/dist/bases-view/list-view/index.d.ts +10 -0
  95. package/dist/bases-view/list-view/index.js +17 -0
  96. package/dist/bases-view/list-view/list.svelte +156 -0
  97. package/dist/bases-view/list-view/list.svelte.d.ts +7 -0
  98. package/dist/bases-view/map-view/index.d.ts +10 -0
  99. package/dist/bases-view/map-view/index.js +17 -0
  100. package/dist/bases-view/map-view/view.svelte +28 -0
  101. package/dist/bases-view/map-view/view.svelte.d.ts +7 -0
  102. package/dist/bases-view/metadata-invalidation-core.d.ts +34 -0
  103. package/dist/bases-view/metadata-invalidation-core.js +162 -0
  104. package/dist/bases-view/metadata-type-info.d.ts +7 -0
  105. package/dist/bases-view/metadata-type-info.js +10 -0
  106. package/dist/bases-view/models.d.ts +90 -0
  107. package/dist/bases-view/models.js +133 -0
  108. package/dist/bases-view/query-result-core.d.ts +8 -0
  109. package/dist/bases-view/query-result-core.js +16 -0
  110. package/dist/bases-view/resource-url.d.ts +2 -0
  111. package/dist/bases-view/resource-url.js +21 -0
  112. package/dist/bases-view/search-core.d.ts +9 -0
  113. package/dist/bases-view/search-core.js +20 -0
  114. package/dist/bases-view/summary-core.d.ts +7 -0
  115. package/dist/bases-view/summary-core.js +17 -0
  116. package/dist/bases-view/table-view/components/div-table.svelte +464 -0
  117. package/dist/bases-view/table-view/components/div-table.svelte.d.ts +32 -0
  118. package/dist/bases-view/table-view/components/dnd-style.d.ts +3 -0
  119. package/dist/bases-view/table-view/components/dnd-style.js +6 -0
  120. package/dist/bases-view/table-view/components/sort-header.svelte +48 -0
  121. package/dist/bases-view/table-view/components/sort-header.svelte.d.ts +11 -0
  122. package/dist/bases-view/table-view/components/table-column-tracks.d.ts +14 -0
  123. package/dist/bases-view/table-view/components/table-column-tracks.js +27 -0
  124. package/dist/bases-view/table-view/components/table-header.svelte +146 -0
  125. package/dist/bases-view/table-view/components/table-header.svelte.d.ts +10 -0
  126. package/dist/bases-view/table-view/components/table-placeholder.svelte +46 -0
  127. package/dist/bases-view/table-view/components/table-placeholder.svelte.d.ts +30 -0
  128. package/dist/bases-view/table-view/components/table-virtualizer-core.d.ts +13 -0
  129. package/dist/bases-view/table-view/components/table-virtualizer-core.js +12 -0
  130. package/dist/bases-view/table-view/components/table.svelte +997 -0
  131. package/dist/bases-view/table-view/components/table.svelte.d.ts +32 -0
  132. package/dist/bases-view/table-view/index.svelte.d.ts +10 -0
  133. package/dist/bases-view/table-view/index.svelte.js +30 -0
  134. package/dist/bases-view/unknown-view/index.d.ts +10 -0
  135. package/dist/bases-view/unknown-view/index.js +19 -0
  136. package/dist/bases-view/unknown-view/view.svelte +11 -0
  137. package/dist/bases-view/unknown-view/view.svelte.d.ts +6 -0
  138. package/dist/bases-view/view-header.svelte +142 -0
  139. package/dist/bases-view/view-header.svelte.d.ts +28 -0
  140. package/dist/bases-view/view-registration-inventory.d.ts +22 -0
  141. package/dist/bases-view/view-registration-inventory.js +7 -0
  142. package/dist/bases-view/view-settings.svelte +350 -0
  143. package/dist/bases-view/view-settings.svelte.d.ts +9 -0
  144. package/dist/bases-view/view.svelte +192 -0
  145. package/dist/bases-view/view.svelte.d.ts +15 -0
  146. package/dist/bases-view-surface.svelte +31 -0
  147. package/dist/bases-view-surface.svelte.d.ts +14 -0
  148. package/dist/index.d.ts +10 -0
  149. package/dist/index.js +8 -0
  150. package/dist/serialized-write-queue.d.ts +10 -0
  151. package/dist/serialized-write-queue.js +36 -0
  152. package/dist/styles.css +2164 -0
  153. package/manifest.json +33 -0
  154. package/package.json +90 -0
  155. package/styles.css +1 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ # Bases changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial independent public package extracted from Lapis Notes without changing
6
+ the runtime plugin ID.
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
+ }