Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ When implementation decisions affect compatibility, runtime behaviour, performan

- `this` is a file-like record. With `--base`/`basePath` it is a synthetic record of the base file itself (path/folder resolved relative to the vault root; no file read); in `--yaml`/flag/library mode without a base path it is an empty record at the vault root. This diverges from Obsidian embed-location semantics but supports the documented patterns (`this.file.folder`, `file.hasLink(this.file)`, `author == this`).
- `file.folder` is supported and resolves to the vault-relative parent directory (`""` for root-level notes).
- `file.ext` is the extension without a leading dot (`"md"`, `"base"`), matching Obsidian, so `file.ext == "md"` works. Applies to indexed notes, `this.file`, and link-derived file records. Runtime adapters' `extname` still returns the dotted form; the dot is stripped in core.
- `file.backlinks` is computed during indexing from resolved `file.links` targets using unambiguous basename / exact path matching.
- `random()` is deliberately not implemented for deterministic query evaluation.
- Non-standard extension globals beyond the Obsidian function surface: `sum(values)`, `avg(values)`, `count(values)`, `contains(container, needle)`. List stat methods `values.mean()`, `values.sum()`, `values.min()`, `values.max()` are supported per official docs.
Expand Down
2 changes: 1 addition & 1 deletion src/core/expression/evaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,7 @@ function createSyntheticFile(pathLike: string): Record<string, unknown> {
const folderIndex = path.lastIndexOf("/");
const folder = folderIndex === -1 ? "" : path.slice(0, folderIndex);
const dotIndex = name.lastIndexOf(".");
const ext = dotIndex === -1 ? "" : name.slice(dotIndex);
const ext = dotIndex === -1 ? "" : name.slice(dotIndex + 1);

return {
name,
Expand Down
2 changes: 1 addition & 1 deletion src/core/vault-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export async function indexVault(options: VaultIndexOptions): Promise<VaultIndex
basename: basenameFromPath(relativePath),
path: relativePath,
folder: folderFromPath(relativePath),
ext: options.adapter.extname(relativePath),
ext: options.adapter.extname(relativePath).replace(/^\./, ""),
size: entry.stat.size,
ctime: entry.stat.ctime,
mtime: entry.stat.mtime,
Expand Down
2 changes: 1 addition & 1 deletion src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function resolveThisFile(options: QueryBaseOptions, adapter: RuntimeAdapter, dir
const folder = slashIndex === -1 ? "" : relativePath.slice(0, slashIndex);
const dotIndex = name.lastIndexOf(".");
const basename = dotIndex === -1 ? name : name.slice(0, dotIndex);
const ext = dotIndex === -1 ? "" : name.slice(dotIndex);
const ext = dotIndex === -1 ? "" : name.slice(dotIndex + 1);

return {
name,
Expand Down
2 changes: 1 addition & 1 deletion test-runtime/conformance-tests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ views:
basename: "a",
path: "a.md",
folder: "",
ext: ".md",
ext: "md",
size: 1,
ctime: new Date(0),
mtime: new Date(0),
Expand Down
4 changes: 2 additions & 2 deletions tests/expression-evaluator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe("expression evaluator", () => {
basename: "note",
path: "projects/note.md",
folder: "projects",
ext: ".md",
ext: "md",
size: 10,
ctime: new Date("2024-01-01"),
mtime: new Date("2024-01-02"),
Expand Down Expand Up @@ -161,7 +161,7 @@ describe("expression evaluator", () => {
basename: "beta",
path: "beta.md",
folder: "",
ext: ".md",
ext: "md",
size: 10,
ctime: new Date("2024-01-01"),
mtime: new Date("2024-01-02"),
Expand Down
39 changes: 35 additions & 4 deletions tests/query-engine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe("query engine", () => {
basename: (path.split("/").pop() ?? path).replace(/\.md$/, ""),
path,
folder: path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : "",
ext: ".md",
ext: "md",
size: 1,
ctime: new Date(0),
mtime: new Date(0),
Expand Down Expand Up @@ -287,6 +287,37 @@ views:
expect(result.diagnostics.errors).toHaveLength(0);
});

test("file.ext is the extension without a leading dot, as in Obsidian", async () => {
const spec = parseBaseYaml(
`
filters: file.ext == "md"
views:
- type: table
name: exts
properties:
- file.name
- file.ext
`.trim(),
);

const compiled = compileQuery(spec);
const indexed = await indexVault({
rootDir: vaultDir,
include: ["**/*.md"],
exclude: [],
adapter: nodeAdapter,
});

const result = executeCompiledQuery({
compiled,
documents: indexed.documents,
view: "exts",
});

expect(result.rows).toHaveLength(indexed.documents.length);
expect(result.rows[0].projected["file.ext"]).toBe("md");
});

test("strict mode rejects identifiers that exist nowhere in the vault", async () => {
const spec = parseBaseYaml(
`
Expand Down Expand Up @@ -456,7 +487,7 @@ views:
test("this is a file-like object, stable across rows", async () => {
const spec = parseBaseYaml(
`
filters: this.file.ext == ".base"
filters: this.file.ext == "base"
views:
- type: table
name: default
Expand All @@ -482,7 +513,7 @@ views:
basename: "base",
path: "queries/base.base",
folder: "queries",
ext: ".base",
ext: "base",
size: 0,
ctime: new Date(0),
mtime: new Date(0),
Expand Down Expand Up @@ -528,7 +559,7 @@ views:
basename: "beta",
path: "beta.md",
folder: "",
ext: ".md",
ext: "md",
size: 0,
ctime: new Date(0),
mtime: new Date(0),
Expand Down
2 changes: 1 addition & 1 deletion tests/serialize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const fixtureResult: QueryResult = {
name: "alpha.md",
path: "alpha.md",
folder: "",
ext: ".md",
ext: "md",
size: 10,
ctime: new Date("2024-01-01"),
mtime: new Date("2024-01-01"),
Expand Down
Loading