
// Type-32/obsidian-bases-parser
Obsidian Bases Parser
A complete TypeScript implementation for Obsidian Bases (.base files) including schema definitions, lexer, parser, evaluator, and reactive query system.
Obsidian Bases TypeScript Schema
A complete TypeScript implementation for Obsidian Bases (.base files) including schema definitions, lexer, parser, evaluator, and reactive query system.
Based on the official Obsidian Bases documentation: https://help.obsidian.md/bases
Overview
This package provides:
- Complete TypeScript Schema - Type definitions for all Obsidian Bases constructs
- Lexer & Parser - Tokenize and parse filter/formula expressions into AST
- Evaluator - Execute parsed expressions against file/note contexts
- Builder APIs - Fluent interfaces for creating bases and filters
- Preset Utilities - Common filter patterns, formulas, and summaries
- YAML Serialization - Convert base objects to valid YAML
- Reactive Query System - Vue-integrated reactive querying with
useBaseQuery()composable - YAML Reader - Parse
.basefiles from YAML strings
Installation
# Using npm
npm install @type32/obsidian-bases-parser
# Using yarn
yarn add @type32/obsidian-bases-parser
# Using pnpm
pnpm add @type32/obsidian-bases-parser
# Using bun
bun add @type32/obsidian-bases-parser
Dependencies
This package requires:
vue^3.5.27 - For reactive systemjs-yaml^4.1.1 - For efficient YAML parsing and serialization
Quick Start
Creating a Base (Static)
import { createBase, PresetFilters, PresetFormulas } from './index';
const myBase = createBase()
.withFilters(PresetFilters.byTag('project'))
.addFormula('days_old', PresetFormulas.daysOld())
.addFormula('status_icon', PresetFormulas.statusIcon())
.addTableView('Active Projects', {
order: ['file.name', 'status', 'formula.status_icon'],
filters: PresetFilters.byStatus('active'),
})
.build();
Creating a Reactive Base (Recommended)
import { createReactiveBase, PresetFilters, PresetFormulas } from './index';
const myBase = createReactiveBase()
.addFilter(PresetFilters.byTag('project'))
.addFormula('days_old', PresetFormulas.daysOld())
.addFormula('status_icon', PresetFormulas.statusIcon())
.addTableView('Active Projects', {
order: ['file.name', 'status', 'formula.status_icon'],
filters: PresetFilters.byStatus('active'),
});
// Can be modified later!
myBase.addFormula('priority_label', PresetFormulas.priorityLabel());
myBase.setViewOrder('Active Projects', ['formula.priority_label', 'file.name']);
// Convert to YAML
const yaml = myBase.toYAML();
Parsing and Evaluating Filters
import { parseFilter, parseFilterExpression, evaluateFilter, validateFilter } from './index';
// Parse a filter expression into a FilterExpObject with extracted components
const parsed = parseFilterExpression('file.hasTag("book") && status == "reading"');
// Access extracted components for programmatic use
if (parsed.fileMethods.includes('hasTag')) {
console.log('Tags referenced:', parsed.referencedTags); // ['book']
}
if (parsed.noteProperties.includes('status')) {
console.log('Filter uses status property');
}
for (const comparison of parsed.comparisons) {
console.log(`${comparison.left.name} ${comparison.operator} ${comparison.right.raw}`);
}
// Parse into AST (lower-level)
const result = parseFilter('file.hasTag("book") && status == "reading"');
// Evaluate against a context
const context = {
file: {
name: 'My Book.md',
tags: ['book', 'reading'],
// ... other file properties
},
note: {
status: 'reading',
},
};
const evaluation = evaluateFilter('file.hasTag("book")', context);
console.log(evaluation); // { type: 'BOOLEAN', value: true }
// Validate a filter
const validation = validateFilter({
and: ['status == "done"', 'priority > 3'],
});
Serializing to YAML
import { serializeToYAML } from './index';
const yaml = serializeToYAML(myBase);
console.log(yaml);
// Output:
// filters:
// and:
// - file.hasTag("project")
// formulas:
// days_old: '((now() - file.ctime) / 86400000).round(0)'
// ...
Note: YAML parsing and serialization is powered by js-yaml for robust, efficient, and standards-compliant handling of YAML files.
Schema Overview
Filter System
Filters can be:
- Simple expressions:
'status == "done"' - Filter objects:
{ and: [...], or: [...], not: [...] } - Nested combinations: Complex boolean logic
import { Filter, FilterObject } from './index';
const simpleFilter: Filter = 'status == "done"';
const complexFilter: Filter = {
or: [
'file.hasTag("important")',
{
and: [
'priority >= 3',
{
not: ['file.hasTag("archived")'],
},
],
},
],
};
View System
Views define how data is displayed:
import { View, TableView, CardsView, ListView, MapView } from './index';
const tableView: TableView = {
type: 'table',
name: 'My Table',
order: ['file.name', 'status', 'due'],
filters: { and: ['status != "done"'] },
groupBy: { property: 'status', direction: 'ASC' },
summaries: { priority: 'Average' },
sort: [
{ property: 'file.name', direction: 'ASC' },
{ property: 'due', direction: 'DESC' },
],
columnSize: {
'file.name': 300,
'status': 150,
},
};
const cardsView: CardsView = {
type: 'cards',
name: 'Project Cards',
order: ['priority', 'file.name'],
cardSize: 250,
image: 'cover',
imageFit: 'cover',
imageAspectRatio: 1.4,
};
Extended View Properties
Table Views:
sort- Separate sorting configuration (different from display order)columnSize- Column width configuration (property name to width in pixels)
Cards Views:
cardSize- Card dimensions in pixelsimage- Image source ('cover','first', or property name)imageFit- Image fitting mode ('cover','contain','fill', or'')imageAspectRatio- Image aspect ratio (number)
Formula System
Formulas compute values from properties:
import { Formulas } from './index';
const formulas: Formulas = {
days_until_due: 'if(due, ((date(due) - today()) / 86400000).round(0), "")',
is_overdue: 'if(due, date(due) < today() && status != "done", false)',
priority_label: 'if(priority == 1, "๐ด High", if(priority == 2, "๐ก Medium", "๐ข Low"))',
};
Parser Features
The parser supports all Obsidian Bases syntax:
Operators
- Comparison:
==,!=,>,<,>=,<= - Arithmetic:
+,-,*,/,% - Boolean:
&&,||,!
Literals
- Strings:
"hello",'world' - Numbers:
42,3.14 - Booleans:
true,false - Durations:
"1d","2h","3w" - Regular Expressions:
/pattern/flags
Property Access
- Dot notation:
file.name,note.status - Bracket notation:
properties["key"],tags[0] - Prefixed:
file.*,note.*,formula.*,this.*
Function Calls
- Global:
now(),today(),date("2024-01-01") - Method:
file.hasTag("tag"),string.contains("sub") - Chained:
file.ctime.format("YYYY-MM-DD")
Special Syntax
- Conditional:
if(condition, trueValue, falseValue) - Date arithmetic:
now() + "1d",today() - "1w"
Preset Utilities
Filters
import { PresetFilters } from './index';
PresetFilters.byTag('project'); // 'file.hasTag("project")'
PresetFilters.byExtension('md'); // 'file.ext == "md"'
PresetFilters.inFolder('Notes'); // 'file.inFolder("Notes")'
PresetFilters.modifiedWithin(7); // 'file.mtime > now() - "7d"'
PresetFilters.byProperty('status', 'active'); // 'status == "active"'
Formulas
import { PresetFormulas } from './index';
PresetFormulas.daysUntilDue(); // Days until due date
PresetFormulas.isOverdue(); // Check if overdue
PresetFormulas.lastModified(); // Relative modification time
PresetFormulas.priorityLabel(); // Priority with icons
PresetFormulas.statusIcon(); // Status with icons
PresetFormulas.formatCurrency('price'); // Format as currency
Summaries
import { PresetSummaries } from './index';
PresetSummaries.customAverage(2); // Average with rounding
PresetSummaries.countFilled(); // Count non-empty values
PresetSummaries.percentTrue(); // Percentage of true values
PresetSummaries.uniqueJoin(', '); // Join unique values
Builder APIs
Base Builder
import { createBase } from './index';
const base = createBase()
.withFilters({ and: ['file.hasTag("task")'] })
.addFormula('days_old', '((now() - file.ctime) / 86400000).round(0)')
.configureProperty('status', { displayName: 'Status' })
.addTableView('Active Tasks', {
order: ['file.name', 'status'],
filters: { and: ['status != "done"'] },
})
.addCardsView('Task Cards', {
order: ['file.name', 'status'],
})
.build();
Filter Builder
import { createFilter, and, or, not } from './index';
const filter = createFilter()
.and('file.hasTag("project")')
.and('file.ext == "md"')
.not('file.hasTag("archived")')
.build();
// Or start with a specific operator
const filter2 = and(
'file.hasTag("important")',
or('priority == 1', 'priority == 2')
);
Complete Base Example
import {
createBase,
PresetFilters,
PresetFormulas,
serializeToYAML,
} from './index';
const taskBase = createBase()
.withFilters(
and(
PresetFilters.byTag('task'),
PresetFilters.byExtension('md')
)
)
.addFormula('days_until_due', PresetFormulas.daysUntilDue())
.addFormula('is_overdue', PresetFormulas.isOverdue())
.addFormula('priority_label', PresetFormulas.priorityLabel())
.configureProperty('formula.days_until_due', { displayName: 'Days Left' })
.addTableView('Active Tasks', {
order: ['file.name', 'formula.priority_label', 'due'],
filters: not(PresetFilters.byStatus('done')),
groupBy: { property: 'status', direction: 'ASC' },
})
.addTableView('Completed Tasks', {
order: ['completed_date', 'file.name'],
filters: PresetFilters.byStatus('done'),
})
.build();
console.log(serializeToYAML(taskBase));
File Structure
obsidian-bases-parser/
โโโ src/
โ โโโ index.ts # Main exports
โ โโโ obsidian-bases-schema.ts # Type definitions
โ โโโ obsidian-bases-parser.ts # Lexer, parser, evaluator
โ โโโ obsidian-bases-utils.ts # Builders, presets, serialization
โ โโโ obsidian-bases-reactive.ts # Reactive system (Vue + ReactiveBase)
โ โโโ obsidian-bases-example.ts # Usage examples
โโโ examples/
โ โโโ reactive-base-example.ts # Comprehensive reactive base examples
โ โโโ simple-reactive-demo.ts # Simple demonstration
โโโ llms.txt # LLM overview
โโโ llms-full.txt # Complete LLM documentation
โโโ README.md # This file
API Reference
Schema Types
| Type | Description |
|---|---|
ObsidianBase | Complete base file structure |
Filter | Filter expression or filter object |
FilterObject | Recursive and/or/not filter structure |
FilterExpObject | Parsed filter with extracted components |
PropertyReference | Property reference with prefix and name |
FunctionCall | Function call with name and arguments |
ComparisonOperation | Comparison with operator and operands |
View | View configuration (table, cards, list, map) |
Formulas | Formula definitions |
Properties | Property display configurations |
Summaries | Custom summary formulas |
FilterExpObject Properties
When you parse a filter expression using parseFilterExpression(), you get a FilterExpObject with these accessible properties:
| Property | Type | Description |
|---|---|---|
raw | string | The original filter expression |
expressionType | ExpressionType | Type: 'comparison', 'boolean', 'function_call', etc. |
ast | ASTExpression | The parsed AST |
properties | PropertyReference[] | All property references |
functionCalls | FunctionCall[] | All function calls |
comparisons | ComparisonOperation[] | All comparison operations |
literals | LiteralValue[] | All literal values |
hasBooleanOperators | boolean | Has &&, ` |
hasDateArithmetic | boolean | Has date arithmetic like now() - "7d" |
usesThisReference | boolean | Uses this keyword |
fileMethods | string[] | File methods used (e.g., 'hasTag', 'inFolder') |
noteProperties | string[] | Note properties referenced |
fileProperties | string[] | File properties referenced |
formulaProperties | string[] | Formula properties referenced |
referencedTags | string[] | Tags from file.hasTag() calls |
referencedFolders | string[] | Folders from file.inFolder() calls |
isValid | boolean | Whether parsing succeeded |
errors | string[] | Parse errors if any |
Parser Classes
| Class | Description |
|---|---|
Lexer | Tokenizes expressions into tokens |
Parser | Builds AST from tokens |
Evaluator | Executes AST against context |
FilterObjectParser | Parses recursive filter objects |
FilterExpressionParser | Parses filter strings into FilterExpObject |
Validator | Validates filter/formula syntax |
Builder Classes
| Class | Description |
|---|---|
BaseBuilder | Fluent API for creating bases |
FilterBuilder | Fluent API for building filters |
Utility Functions
| Function | Description |
|---|---|
createBase() | Create a new base builder |
createFilter() | Create a new filter builder |
parseFilter() | Parse a filter to AST |
parseFilterExpression() | Parse filter to FilterExpObject with components |
parseFormula() | Parse a formula to AST |
evaluateFilter() | Evaluate a filter against context |
evaluateFormula() | Evaluate a formula against context |
validateFilter() | Validate filter syntax |
validateFormula() | Validate formula syntax |
serializeToYAML() | Convert base to YAML |
readBase() | Parse YAML string into ObsidianBase |
Reactive System (Vue Integration)
| Class/Function | Description |
|---|---|
ReactiveBase | Reactive base object with modification methods |
ReactiveBaseQuery | Reactive query class with automatic re-evaluation |
useBase() | Unified composable for complete base management |
useBaseQuery() | Vue composable for reactive queries |
useBaseView() | Vue composable for single view queries |
createReactiveBase() | Create a new ReactiveBase instance |
createReactiveBaseFromYAML() | Create a ReactiveBase from YAML string |
createBaseQuery() | Create a ReactiveBaseQuery instance |
createBaseQueryFromYAML() | Create a query from YAML string |
Reactive Query System
The reactive query system integrates with Vue's reactivity system to provide automatic updates when source data changes.
Reactive Base Object
The ReactiveBase class provides a reactive, modifiable base configuration with methods to adjust filters, views, formulas, and more in real-time.
import { createReactiveBase, ReactiveBaseQuery } from './index';
// Create a reactive base
const base = createReactiveBase();
// Add filters dynamically
base.addFilter('file.hasTag("task")');
base.addFilter('status != "done"');
// Add formulas
base.addFormula('days_old', '((now() - file.ctime) / 86400000).round(0)');
// Add views
base.addTableView('Active Tasks', {
order: ['file.name', 'formula.days_old'],
filters: 'priority > 2',
});
// Use with reactive query
const query = new ReactiveBaseQuery(source, base.ref);
const results = query.getViewResults('Active Tasks');
// Modify the base - results automatically update!
base.setViewFilters('Active Tasks', 'priority > 1');
base.addFormula('priority_label', 'if(priority == 1, "High", "Low")');
base.setViewOrder('Active Tasks', ['formula.priority_label', 'file.name']);
// Convert to YAML for saving
const yaml = base.toYAML();
ReactiveBase API
The ReactiveBase class provides comprehensive methods for modifying base configurations:
Filters
setFilters(filters)- Replace all global filtersaddFilter(expression)- Add a filter (AND with existing)clearFilters()- Remove all global filters
Formulas
addFormula(name, expression)- Add or update a formularemoveFormula(name)- Remove a formulasetFormulas(formulas)- Replace all formulasclearFormulas()- Remove all formulas
Properties
configureProperty(name, config)- Configure property display settingsremovePropertyConfig(name)- Remove property configurationsetProperties(properties)- Replace all property configurationsclearProperties()- Remove all property configurations
Summaries
addSummary(name, expression)- Add or update a custom summaryremoveSummary(name)- Remove a summarysetSummaries(summaries)- Replace all summariesclearSummaries()- Remove all summaries
Views
addView(view)- Add a viewaddTableView(name, options)- Add a table viewaddCardsView(name, options)- Add a cards viewaddListView(name, options)- Add a list viewaddMapView(name, options)- Add a map viewremoveView(name)- Remove a view by nameupdateView(name, updater)- Update a view by namegetView(name)- Get a view configurationsetViewFilters(viewName, filters)- Update view filterssetViewOrder(viewName, order)- Update view ordersetViewLimit(viewName, limit)- Update view limitsetViewGroupBy(viewName, groupBy)- Update view groupingclearViews()- Remove all views
Utility Methods
toYAML()- Convert to YAML stringfromYAML(yaml)- Load from YAML stringclone()- Create a copy of the basereset()- Reset to empty basetoObject()- Get plain object (non-reactive copy)
Dynamic Base Modifications
The key advantage of ReactiveBase is that all modifications are reactive and automatically propagate to any ReactiveBaseQuery instances using it:
import { ref } from 'vue';
import { createReactiveBase, ReactiveBaseQuery, BaseSource } from './index';
const source = ref<BaseSource[]>([/* your data */]);
const base = createReactiveBase();
// Initial setup
base.addFilter('file.hasTag("project")')
.addTableView('Projects', { order: ['file.name'] });
// Create query
const query = new ReactiveBaseQuery(source, base.ref);
const results = query.getViewResults('Projects');
// Later: Add a new formula
base.addFormula('status_icon', 'if(status == "done", "โ
", "โณ")');
// Update view to use new formula
base.setViewOrder('Projects', ['formula.status_icon', 'file.name']);
// Results automatically update with new formula and ordering!
console.log(results.value.items);
// Later: Change filters
base.clearFilters();
base.addFilter('file.hasTag("active-project")');
// Results automatically re-filter!
console.log(results.value.items);
Loading from YAML
You can create a reactive base from existing YAML:
import { createReactiveBaseFromYAML } from './index';
const yaml = `
filters:
and:
- file.hasTag("task")
formulas:
days_old: '((now() - file.ctime) / 86400000).round(0)'
views:
- type: table
name: "Tasks"
order:
- file.name
`;
const base = createReactiveBaseFromYAML(yaml);
// Now modify it
base.addFormula('priority_label', 'if(priority == 1, "High", "Low")');
base.addTableView('High Priority', {
filters: 'priority == 1',
order: ['file.name'],
});
// Save back to YAML
const updatedYaml = base.toYAML();
Builder Pattern vs Reactive Base
Static Builder (Old Approach)
import { createBase } from './index';
// Build once
const base = createBase()
.addFilter('file.hasTag("task")')
.addFormula('days_old', '((now() - file.ctime) / 86400000).round(0)')
.addTableView('Tasks', { order: ['file.name'] })
.build(); // Returns a plain object
// โ Cannot modify after build
// You would need to rebuild from scratch
// Use with query
const query = new ReactiveBaseQuery(source, base);
Reactive Base (New Approach)
import { createReactiveBase } from './index';
// Create reactive base
const base = createReactiveBase()
.addFilter('file.hasTag("task")')
.addFormula('days_old', '((now() - file.ctime) / 86400000).round(0)')
.addTableView('Tasks', { order: ['file.name'] });
// No .build() needed - stays reactive!
// โ
Can modify anytime
base.addFormula('priority_label', 'if(priority == 1, "High", "Low")');
base.setViewOrder('Tasks', ['formula.priority_label', 'file.name']);
// Use with query - changes propagate automatically
const query = new ReactiveBaseQuery(source, base.ref);
const results = query.getViewResults('Tasks');
// Modifications automatically update results!
base.setViewFilters('Tasks', 'status != "done"');
console.log(results.value.items); // Automatically filtered!
Key Differences
| Feature | Static Builder | Reactive Base |
|---|---|---|
| Modification | โ Immutable after .build() | โ Fully mutable |
| Reactivity | โ No automatic updates | โ Automatic updates |
| YAML Export | โ
Via serializeToYAML() | โ
Via .toYAML() |
| YAML Import | โ
Via readBase() | โ
Via .fromYAML() |
| Use Case | Static configurations | Dynamic, real-time updates |
| Return Type | Plain ObsidianBase object | ReactiveBase instance |
Recommendation: Use ReactiveBase when you need dynamic configuration changes, especially in interactive applications. Use the static builder when you have fixed configurations that won't change.
Reactive Query System
The reactive query system integrates with Vue's reactivity system to provide automatic updates when source data changes.
Basic Usage
import { ref } from 'vue';
import { readBase, useBaseQuery, BaseSource } from './index';
// Define your data type
interface Task {
title: string;
assignee: string;
}
// Create reactive source
const source = ref<BaseSource<Task>[]>([
{
id: '1',
name: 'Task 1.md',
basename: 'Task 1',
tags: ['task', 'urgent'],
properties: { status: 'todo', priority: 1 },
data: { title: 'Complete docs', assignee: 'Alice' },
},
// ... more items
]);
// Read base from YAML
const yaml = `
filters:
and:
- file.hasTag("task")
formulas:
priority_label: 'if(priority == 1, "๐ด High", "๐ก Medium")'
views:
- type: table
name: "Active Tasks"
filters:
and:
- status != "done"
order: [file.name, formula.priority_label]
`;
const base = readBase(yaml);
// Create reactive query
const { getViewResults, source: reactiveSource } = useBaseQuery(source, base);
// Get reactive results
const activeTasks = getViewResults('Active Tasks');
// Access in template or computed
// activeTasks.value.items - filtered, sorted items
// activeTasks.value.totalCount - total before limit
// activeTasks.value.groups - grouped items (if groupBy specified)
// activeTasks.value.summaries - calculated summaries
Vue Component Example
<template>
<div>
<h1>Tasks ({{ taskCount }})</h1>
<ul>
<li v-for="task in tasks" :key="task.id">
{{ task.properties?.priority_label }} - {{ task.data?.title }}
</li>
</ul>
<button @click="addTask">Add Task</button>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { useBaseView, readBase, BaseSource } from './index';
interface Task {
title: string;
}
const source = ref<BaseSource<Task>[]>([/* ... */]);
const base = readBase(`
formulas:
priority_label: 'if(priority == 1, "๐ด", "๐ข")'
views:
- type: list
name: "Tasks"
order: [formula.priority_label]
`);
// useBaseView returns ComputedRefs that auto-update
const { items: tasks, totalCount: taskCount } = useBaseView(
source,
base,
'Tasks'
);
function addTask() {
source.value.push({
id: String(Date.now()),
name: 'New Task.md',
properties: { priority: 1 },
data: { title: 'New Task' },
});
// tasks and taskCount automatically update!
}
</script>
useBase() - Unified Base Management
The useBase() composable provides a complete, unified interface for managing an entire base instance reactively. It combines base configuration, source data management, and querying into one convenient API.
import { ref } from 'vue';
import { useBase, BaseSource } from './index';
interface Task {
title: string;
description: string;
}
const {
// Base & State
base,
isLoaded,
hasChanges,
state,
// Load/Save
load,
save,
// Source Management
source,
addItem,
updateItem,
removeItem,
// Query Access
getViewResults,
viewNames,
// Shortcuts
addView,
addFormula,
setFilters,
} = useBase<Task>({
source: ref([]),
trackChanges: true,
});
// Load from YAML
load(yamlString);
// Modify base
addFormula('priority_label', 'if(priority == 1, "High", "Low")');
addView({ type: 'table', name: 'Tasks', order: ['file.name'] });
// Add data
addItem({
id: '1',
name: 'Task.md',
properties: { status: 'todo' },
data: { title: 'My Task', description: 'Details' },
});
// Query
const tasks = getViewResults('Tasks');
console.log(tasks.value.items);
// Save
const yaml = save();
Features
- Unified API - One composable for everything
- Reactive - All changes propagate automatically
- Change Tracking - Know when there are unsaved changes
- Source Management - CRUD operations on source data
- Full Query Access - All reactive query features
- Load/Save - YAML import/export
- State & Statistics - View counts, item counts, etc.
Auto-save Pattern
const { save, hasChanges } = useBase({
base: yamlString,
trackChanges: true,
});
// Implement auto-save with debouncing
let saveTimeout: NodeJS.Timeout | null = null;
watch(hasChanges, (changed) => {
if (changed) {
if (saveTimeout) clearTimeout(saveTimeout);
saveTimeout = setTimeout(async () => {
const yaml = save();
await writeFile('config.base', yaml);
}, 1000); // Save after 1s of no changes
}
});
See examples/use-base-example.ts and examples/use-base-vue-component.vue for complete examples.
Using ReactiveBaseQuery Class
import { ref } from 'vue';
import { ReactiveBaseQuery, readBase, BaseSource } from './index';
const source = ref<BaseSource[]>([/* ... */]);
const base = readBase(yamlString);
// Create query instance
const query = new ReactiveBaseQuery(source, base, { debug: true });
// Get computed results for different views
const tableResults = query.getViewResults('Table View');
const cardResults = query.getViewResults('Card View');
// Access reactive data
console.log(tableResults.value.items);
console.log(tableResults.value.totalCount);
console.log(tableResults.value.summaries);
// Update source - results auto-update
query.setSource([...newItems]);
// Or update base - results auto-update
query.setBase(newBase);
// Force refresh
query.refresh();
BaseSource Type
The BaseSource<T> type is flexible and allows custom data:
interface BaseSource<T = void> {
// Obsidian-specific fields (all optional)
id?: string;
name?: string; // File name (e.g., "My Note.md")
basename?: string; // Without extension (e.g., "My Note")
path?: string; // Full path (e.g., "Notes/My Note.md")
folder?: string; // Parent folder (e.g., "Notes")
ext?: string; // Extension (e.g., "md")
size?: number; // File size in bytes
ctime?: Date | string; // Created time
mtime?: Date | string; // Modified time
tags?: string[]; // Tags
links?: string[]; // Internal links
backlinks?: string[]; // Backlinks
embeds?: string[]; // Embeds
properties?: Record<string, unknown>; // Frontmatter properties
// Your custom data
data?: T;
}
Custom Formula Functions
const query = new ReactiveBaseQuery(source, base, {
customFunctions: {
calculateScore: (priority: number, complexity: number) => {
return priority * complexity * 10;
},
formatDate: (date: Date, format: string) => {
// Your date formatting logic
return formattedDate;
},
},
});
License
MIT

