@blacksmithgu/datacore
Version:
Reactive data engine for Obsidian.md.
1,056 lines (1,005 loc) • 113 kB
TypeScript
import { App } from 'obsidian';
import { Component } from 'obsidian';
import { ComponentChildren } from 'preact';
import { CSSProperties } from 'preact/compat';
import { DateTime } from 'luxon';
import { Duration } from 'luxon';
import { EventRef } from 'obsidian';
import { Events } from 'obsidian';
import { FileManager } from 'obsidian';
import * as hooks from 'preact/hooks';
import { HTMLAttributes } from 'preact/compat';
import { JSX as JSX_2 } from 'preact/compat';
import { JSX as JSX_3 } from 'preact/jsx-runtime';
import { JSX as JSX_4 } from 'preact';
import * as luxon_2 from 'luxon';
import { MarkdownPostProcessorContext } from 'obsidian';
import { MarkdownRenderChild } from 'obsidian';
import { MetadataCache } from 'obsidian';
import { Plugin as Plugin_2 } from 'obsidian';
import * as preact_2 from 'preact';
import { PropsWithChildren } from 'preact/compat';
import { Queue } from '@datastructures-js/queue';
import { default as React_2 } from 'preact/compat';
import { ReactNode } from 'preact/compat';
import { TFile } from 'obsidian';
import { Vault } from 'obsidian';
import { VNode } from 'preact';
/** Arithmetic operators which yield numbers and other values. */
export declare type ArithmeticOp = "+" | "-" | "*" | "/" | "%" | "&" | "|";
/** @public A function which compares two types. */
export declare type ArrayComparator<T> = (a: T, b: T) => number;
/** @public A function which maps an array element to some value. */
export declare type ArrayFunc<T, O> = (elem: T, index: number, arr: T[]) => O;
/** Shared metadata for all canvas cards. */
export declare abstract class BaseCanvasCard implements Indexable, Linkable {
abstract $types: string[];
abstract $typename: string;
abstract readonly $type: string;
$revision?: number | undefined;
$id: string;
$position: CardPos;
$dimensions: CardDimensions;
$parent?: Indexable;
$file: string;
$color?: string;
constructor(init: Partial<BaseCanvasCard>);
get $link(): Link;
/** @internal */
json(): JsonBaseCanvasCard;
}
/** All valid binary operators. */
export declare type BinaryOp = CompareOp | ArithmeticOp | LogicalOp;
/** A binary operator expression which combines two subnodes somehow. */
export declare interface BinaryOpExpression {
type: "binaryop";
left: Expression;
right: Expression;
op: BinaryOp;
}
/** Provides implementations for binary operators on two types using a registry. */
declare class BinaryOpHandler {
private map;
private handleDefaultNulls;
static create(): BinaryOpHandler;
constructor();
register<T extends LiteralTypeOrAll, U extends LiteralTypeOrAll>(left: T, op: BinaryOp, right: U, func: BinaryOpImpl<LiteralReprAll<T>, LiteralReprAll<U>>): BinaryOpHandler;
registerResult<T extends LiteralTypeOrAll, U extends LiteralTypeOrAll>(left: T, op: BinaryOp, right: U, func: BinaryOpResultImpl<LiteralReprAll<T>, LiteralReprAll<U>>): BinaryOpHandler;
registerComm<T extends LiteralTypeOrAll, U extends LiteralTypeOrAll>(left: T, op: BinaryOp, right: U, func: BinaryOpImpl<LiteralReprAll<T>, LiteralReprAll<U>>): BinaryOpHandler;
/** If enabled, all null (op) null operations produce null. */
withDefaultNullHandling(): BinaryOpHandler;
/** Implement a comparison function. */
compare<T extends LiteralTypeOrAll>(type: T, compare: CompareImpl<LiteralReprAll<T>>): BinaryOpHandler;
/** Attempt to evaluate the given binary operator on the two literal fields. */
evaluate(op: BinaryOp, left: Literal, right: Literal, ctx: Evaluator): Result<Literal, string>;
/** Create a string representation of the given triplet for unique lookup in the map. */
static repr(op: BinaryOp, left: LiteralTypeOrAll, right: LiteralTypeOrAll): string;
}
/** An implementation for a binary operator. */
declare type BinaryOpImpl<A extends Literal, B extends Literal> = (first: A, second: B, ctx: Evaluator) => Literal;
/** Binary operator which can fail and produce an error. */
declare type BinaryOpResultImpl<A extends Literal, B extends Literal> = (first: A, second: B, ctx: Evaluator) => Result<Literal, string>;
/**
* Wrapper for a regular HTML button with some default classes.
* @group Components
*/
export declare function Button(props: {
className?: string;
intent?: Intent;
children: ComponentChildren;
} & React_2.ComponentProps<"button">): React_2.JSX.Element;
/** A piece of data that has been cached for a specific version and time. */
declare interface Cached<T> {
/** The version of the plugin that the data was written to cache with. */
version: string;
/** The UNIX epoch time in milliseconds that the data was written to cache. */
time: number;
/** The data that was cached. */
data: T;
}
/**
* @group Components
* @param props {@inheritDoc CalloutProps}
*/
export declare function Callout({ collapsible, open: openProp, initialOpen, onOpenChange, title, icon, children, type, }: PropsWithChildren<CalloutProps>): JSX_4.Element;
/** General properties for configuring a callout.
* @group Props
*/
export declare interface CalloutProps {
/** Title of the callout. */
title: string | VNode;
/** Arbitrary icon to show at the left side of the title in the callout. */
icon?: VNode;
/** The type of the callout. */
type?: string;
/** Whether the callout is collapsible (defaults to true). */
collapsible?: boolean;
/** Controlled prop for setting whether the callout is open. */
open: boolean;
/** Whether the callout is initially open if uncontrolled. */
initialOpen?: boolean;
/** Called whenever the open state of the callout changes due to user action. */
onOpenChange?: (value: boolean) => void;
}
/** A canvas file, consisting of a set of canvas cards. */
export declare class Canvas implements Linkable, File_2, Linkbearing, Taggable, Indexable, Fieldbearing {
static TYPES: string[];
$types: string[];
$typename: string;
/** Time that the file was created on the file system. */
$ctime: DateTime;
/** The that the file was last modified on the file system. */
$mtime: DateTime;
/** File extension - for canvas files, generally always 'canvas'. */
$extension: string;
/** The full path of the canvas file. */
get $file(): string;
get $id(): string;
/** A link object pointing to the canvas file. */
get $link(): Link;
$path: string;
$cards: BaseCanvasCard[];
$size: number;
$tags: string[];
$links: Link[];
$infields: Record<string, InlineField>;
private constructor();
get fields(): Field[];
field(key: string): Field | undefined;
json(): JsonCanvas;
static from(raw: JsonCanvas, normalizer?: LinkNormalizer): Canvas;
private static FIELD_DEF;
}
/** All supported canvas card types. */
export declare type CanvasCard = CanvasTextCard | CanvasFileCard | CanvasWebCard;
/** Canvas card that is just a file embedding. */
export declare class CanvasFileCard extends BaseCanvasCard implements Indexable {
static TYPES: string[];
$types: string[];
$typename: string;
private constructor();
readonly $type: string;
$linkedFile: string;
/** @internal */
json(): JsonCanvasFileCard;
/** @internal */
static from(raw: JsonCanvasFileCard): CanvasFileCard;
}
/** Canvas card with markdown text in it. */
export declare class CanvasTextCard extends BaseCanvasCard implements Linkbearing, Taggable, Indexable, Fieldbearing {
static TYPES: string[];
$types: string[];
$typename: string;
$type: string;
$id: string;
$file: string;
$links: Link[];
$tags: string[];
$title: string;
$parent?: Indexable;
$revision?: number;
$infields: Record<string, InlineField>;
$frontmatter?: Record<string, FrontmatterEntry>;
$dimensions: CardDimensions;
$sections: MarkdownSection[];
private constructor();
get fields(): Field[];
field(key: string): Field | undefined;
/** @internal */
json(): JsonCanvasTextCard;
/** @internal */
static from(raw: JsonCanvasTextCard, file: string, normalizer?: LinkNormalizer): CanvasTextCard;
static FIELD_DEF: FieldExtractor<CanvasTextCard>;
}
export declare class CanvasWebCard extends BaseCanvasCard implements Indexable {
static TYPES: string[];
$types: string[];
readonly $type: string;
$typename: string;
$url: string;
private constructor();
/** @internal */
json(): JsonCanvasWebCard;
/** @internal */
static from(raw: JsonCanvasWebCard, file: string): CanvasWebCard;
}
/**
* A card with a title and content
*
* @group Components
*/
export declare function Card<T>(props: CardProps<T>): JSX_4.Element;
/** 2D dimensions of a canvas card in logical units. */
export declare interface CardDimensions {
width: number;
height: number;
}
/** Absolute x, y position of a card on the canvas in logical units. */
export declare interface CardPos {
x: number;
y: number;
}
/**
* Props for the card component
*
* @group Props
*/
export declare interface CardProps<T> {
/** the actual value held in this card. */
value: T;
/** The title of the card. */
title: Literal | ((val: T) => Literal | VNode);
/** The raw content of the card. */
content: Literal | ((val: T) => Literal | VNode);
/** optional footer (because why not?) */
footer?: Literal | ((val: T) => Literal | VNode);
/** If true, the title will be rendered centered. */
centerTitle?: boolean;
}
/**
* A checkbox that can be checked and unchecked.
* @group Components
*/
export declare function Checkbox(props: {
className?: string;
disabled?: boolean;
checked?: boolean;
defaultChecked?: boolean;
onCheckChange?: (checked: boolean) => void;
children?: ComponentChildren;
} & React_2.ComponentProps<"input">): React_2.JSX.Element;
declare namespace Coerce {
/** Coerces common types to string or otherwise undefined. */
function string(value: Literal): string | undefined;
/** Coerces booleans and string-booleans. */
function boolean(value: Literal): boolean | undefined;
/** Coerces numbers and strings to numbers. */
function number(value: Literal): number | undefined;
/** Coerces dates and strings into dates. */
function date(value: Literal): DateTime | undefined;
/** Coerces durations and strings into durations. */
function duration(value: Literal): Duration | undefined;
/** Coerces links and strings into links. */
function link(value: Literal): Link | undefined;
/** Coerces anything into an array, by converting non-arrays into unit length arrays. */
function array(value: Literal): Literal[] | undefined;
}
/**
* Appends additional classes to a basic fixed class.
* @group Utilities
* */
export declare function combineClasses(fixed: string, ...rest: (string | undefined)[]): string;
/** An implementation of a comparator (returning a number) which then automatically defines all of the comparison operators. */
declare type CompareImpl<T extends Literal> = (first: T, second: T, ctx: Evaluator) => number;
/** Comparison operators which yield true/false. */
export declare type CompareOp = ">" | ">=" | "<=" | "<" | "=" | "!=";
/**
* @public Proxied interface which allows manipulating array-based data. All functions on a data array produce a NEW array
* (i.e., the arrays are immutable).
*/
export declare interface DataArray<T> {
/** The total number of elements in the array. */
length: number;
/** Applies the given function to the entire data array. Allows using function chaining while applying an arbitrary intermediate function. */
chain<U>(op: (arr: DataArray<T>) => DataArray<U>): DataArray<U>;
/** Filter the data array down to just elements which match the given predicate. */
where(predicate: ArrayFunc<T, boolean>): DataArray<T>;
/** Alias for 'where' for people who want array semantics. */
filter(predicate: ArrayFunc<T, boolean>): DataArray<T>;
/** Map elements in the data array by applying a function to each. */
map<U>(f: ArrayFunc<T, U>): DataArray<U>;
/** Map elements in the data array by applying a function to each, then flatten the results to produce a new array. */
flatMap<U>(f: ArrayFunc<T, U[]>): DataArray<U>;
/** Mutably change each value in the array, returning the same array which you can further chain off of. */
mutate(f: ArrayFunc<T, void>): DataArray<T>;
/** Limit the total number of entries in the array to the given value. */
limit(count: number): DataArray<T>;
/**
* Take a slice of the array. If `start` is undefined, it is assumed to be 0; if `end` is undefined, it is assumbed
* to be the end of the array.
*/
slice(start?: number, end?: number): DataArray<T>;
/** Concatenate the values in this data array with those of another iterable / data array / array. */
concat(other: Iterable<T>): DataArray<T>;
/** Return the first index of the given (optionally starting the search) */
indexOf(element: T, fromIndex?: number): number;
/** Return the first element that satisfies the given predicate. */
find(pred: ArrayFunc<T, boolean>): T | undefined;
/** Find the index of the first element that satisfies the given predicate. Returns -1 if nothing was found. */
findIndex(pred: ArrayFunc<T, boolean>, fromIndex?: number): number;
/** Returns true if the array contains the given element, and false otherwise. */
includes(element: T): boolean;
/**
* Return a string obtained by converting each element in the array to a string, and joining it with the
* given separator (which defaults to ', ').
*/
join(sep?: string): string;
/**
* Return a sorted array sorted by the given key; an optional comparator can be provided, which will
* be used to compare the keys in leiu of the default dataview comparator.
*/
sort<U>(key: ArrayFunc<T, U>, direction?: "asc" | "desc", comparator?: ArrayComparator<U>): DataArray<T>;
/**
* Mutably modify the current array with an in place sort; this is less flexible than a regular sort in exchange
* for being a little more performant. Only use this is performance is a serious consideration.
*/
sortInPlace<U>(key: (v: T) => U, direction?: "asc" | "desc", comparator?: ArrayComparator<U>): DataArray<T>;
/**
* Return an array where elements are grouped by the given key; the resulting array will have objects of the form
* \`{ key: \<key value\>, rows: DataArray }`.
*/
groupBy<U>(key: ArrayFunc<T, U>, comparator?: ArrayComparator<U>): DataArray<{
key: U;
rows: T[];
}>;
/**
* If the array is not grouped, groups it as `groupBy` does; otherwise, groups the elements inside each current
* group. This allows for top-down recursive grouping which may be easier than bottom-up grouping.
*/
groupIn<U>(key: ArrayFunc<LowestKey<T>, U>, comparator?: ArrayComparator<U>): DataArray<Ingrouped<U, T>>;
/**
* Return distinct entries. If a key is provided, then rows with distinct keys are returned.
*/
distinct<U>(key?: ArrayFunc<T, U>, comparator?: ArrayComparator<U>): DataArray<T>;
/** Return true if the predicate is true for all values. */
every(f: ArrayFunc<T, boolean>): boolean;
/** Return true if the predicate is true for at least one value. */
some(f: ArrayFunc<T, boolean>): boolean;
/** Return true if the predicate is FALSE for all values. */
none(f: ArrayFunc<T, boolean>): boolean;
/** Return the first element in the data array. Returns undefined if the array is empty. */
first(): T | undefined;
/** Return the last element in the data array. Returns undefined if the array is empty. */
last(): T | undefined;
/** Map every element in this data array to the given key, and then flatten it.*/
to(key: string): DataArray<any>;
/** Map every element in this data array to the given key; unlike to(), does not flatten the result. */
into(key: string): DataArray<any>;
/**
* Recursively expand the given key, flattening a tree structure based on the key into a flat array. Useful for handling
* heirarchical data like tasks with 'subtasks'.
*/
expand(key: string): DataArray<any>;
/** Run a lambda on each element in the array. */
forEach(f: ArrayFunc<T, void>): void;
/** Convert this to a plain javascript array. */
array(): T[];
/** Allow iterating directly over the array. */
[Symbol.iterator](): Iterator<T>;
/** Map indexes to values. */
[index: number]: any;
/** Automatic flattening of fields. Equivalent to implicitly calling `array.to("field")` */
[field: string]: any;
}
/** @public Provides utility functions for generating data arrays. */
export declare namespace DataArray {
/** Create a new Dataview data array. */
export function wrap<T>(raw: T[] | DataArray<T>): DataArray<T>;
/** Create a new DataArray from an iterable object. */
export function from<T>(raw: Iterable<T>): DataArray<T>;
/** Return true if the given object is a data array. */
export function isDataArray(obj: any): obj is DataArray<any>;
}
/** Central API object; handles initialization, events, debouncing, and access to datacore functionality. */
export declare class Datacore extends Component {
app: App;
version: string;
settings: Settings;
/** Access to the obsidian vault. */
vault: Vault;
/** Provides access to per-(markdown)-file metadata. */
metadataCache: MetadataCache;
/** Datacore events, mainly used to update downstream views. This object is shadowed by the Datacore object itself. */
events: Events;
/** @internal In-memory index over all stored metadata. */
datastore: Datastore;
/** @internal Asynchronous multi-threaded file importer with throttling. */
importer: FileImporter;
/** @internal Queue of asynchronous read requests; ensures we limit the maximum number of concurrent file loads. */
reads: EmbedQueue;
/** @internal Local-storage backed cache of metadata objects. */
persister: LocalStorageCache;
/** @internal Only set when datacore is in the midst of initialization; tracks current progress. */
initializer?: DatacoreInitializer;
/** If true, datacore is fully hydrated and all files have been indexed. */
initialized: boolean;
constructor(app: App, version: string, settings: Settings);
/** Obtain the current index revision, for determining if anything has changed. */
get revision(): number;
/** Initialize datacore by scanning persisted caches and all available files, and queueing parses as needed. */
initialize(): void;
/** Starts the background initializer. */
index(): void;
private rename;
/**
* Read a file from the Obsidian cache efficiently, limiting the number of concurrent request and debouncing
* multiple requests for the same file.
*/
read(file: TFile): Promise<string>;
/** Queue a file for reloading; this is done asynchronously in the background and may take a few seconds. */
reload(file: TFile): Promise<Indexable>;
/** Store a canvas document. */
storeCanvas(data: Canvas): void;
/** Store a markdown document. */
storeMarkdown(data: MarkdownPage): void;
/** Called whenever the index updates to a new revision. This is the broadest possible datacore event. */
on(evt: "update", callback: (revision: number) => any, context?: any): EventRef;
/** Called whenever datacore records a file rename and has finished reindexing the rename. */
on(evt: "rename", callback: (newPath: string, oldPath: string) => any, context?: any): EventRef;
/** Called when datacore has initialized and is querable. */
on(evt: "initialized", callback: () => any, context?: any): EventRef;
/** Unsubscribe from an event using the event and original callback. */
off(evt: string, callback: (...data: any) => any): void;
/** Unsubscribe from an event using the event reference. */
offref(ref: EventRef): void;
/** Trigger an update event. */
private trigger;
}
/**
* Exterally visible API for datacore.
* @public
*/
export declare class DatacoreApi {
core: Datacore;
constructor(core: Datacore);
/** Get access to luxon functions. */
get luxon(): typeof luxon_2;
/** Get access to preact functions. */
get preact(): typeof preact_2;
/** Central Obsidian app object. */
get app(): App;
/** Construct a local API for the file at the given path. */
local(path: string): DatacoreLocalApi;
/** Load a markdown file by full path or link. */
page(path: string | Link): MarkdownPage | undefined;
/** Execute a textual or typed index query, returning all results. */
query(query: string | IndexQuery): Indexable[];
/** Execute a textual or typed index query, returning all results. */
tryQuery(query: string | IndexQuery): Result<Indexable[], string>;
/** Execute a textual or typed index query, returning results plus performance metadata. */
fullquery(query: string | IndexQuery): SearchResult<Indexable>;
/** Execute a textual or typed index query, returning results plus performance metadata. */
tryFullQuery(query: string | IndexQuery): Result<SearchResult<Indexable>, string>;
/** Utilities for coercing types into one specific type for easier programming. */
coerce: typeof Coerce;
/** Resolve a local or absolute path or link to an absolute path. */
resolvePath(path: string | Link, sourcePath?: string): string;
/** Try to parse the given query, returning a monadic success/failure result. */
tryParseQuery(query: string | IndexQuery): Result<IndexQuery, string>;
/** Try to parse the given query, throwing an error if it is invalid. */
parseQuery(query: string | IndexQuery): IndexQuery;
/** Create a file link pointing to the given path. */
fileLink(path: string): Link;
/** Create a link to a header with the given name. */
headerLink(path: string, header: string): Link;
/** Create a link to a block with the given path and block ID. */
blockLink(path: string, block: string): Link;
/** Try to parse the given link, throwing an error if it is invalid. */
parseLink(linktext: string): Link;
/** Try to parse a link, returning a monadic success/failure result. */
tryParseLink(linktext: string): Result<Link, string>;
/** Create a data array from a regular array. */
array<T>(input: T[] | DataArray<T>): DataArray<T>;
/** Evaluate an expression and return it's evaluated value, throwing an exception on failure. */
evaluate(expression: string | Expression, variables?: Record<string, Literal> | any, sourcePath?: string): Literal;
/** Evaluate an expression and return it's evaluated value. */
tryEvaluate(expression: string | Expression, variables?: Record<string, Literal> | any, sourcePath?: string): Result<Literal, string>;
/**
* Run the given DatacoreJS script, rendering it into the given container. This function
* will return quickly; actual rendering is done asynchronously in the background.
*
* Returns a markdown render child representing the rendered object.
*/
executeJs(source: string, container: HTMLElement, component: Component | MarkdownPostProcessorContext, sourcePath: string): MarkdownRenderChild;
/**
* Similar to `executeJs`, but for JSX scripts. If you are unsure if your input will be JS
* or JSX, use this one, as it also supports regular javascript (albeit at at a mild performance
* hit to rendering).
*/
executeJsx(source: string, container: HTMLElement, component: Component | MarkdownPostProcessorContext, sourcePath: string): MarkdownRenderChild;
/**
* Similar to `executeJs`, but for TypeScript scripts. Use the TSX variant for TSX supprot.
*/
executeTs(source: string, container: HTMLElement, component: Component | MarkdownPostProcessorContext, sourcePath: string): MarkdownRenderChild;
/**
* Similar to `executeTs`, but for TSX scripts. If you are unsure if your input will be TS
* or TSX, use this one, as it also supports regular javascript (albeit at at a mild performance
* hit to rendering).
*
* This generally will also work if you are unsure if your input is javascript or typescript,
* though beware there are a few niche cases where javascript and typescript diverge in syntax.
*/
executeTsx(source: string, container: HTMLElement, component: Component | MarkdownPostProcessorContext, sourcePath: string): MarkdownRenderChild;
/** Shared logic for rendering any JS/TS script. */
private _renderJavascript;
}
/** Lifecycle-respecting file queue which will import files, reading them from the file cache if needed. */
declare class DatacoreInitializer extends Component {
core: Datacore;
/** Number of concurrent operations the initializer will perform. */
static BATCH_SIZE: number;
/** Whether the initializer should continue to run. */
active: boolean;
/** Queue of files to still import. */
queue: TFile[];
/** The files actively being imported. */
current: TFile[];
/** Deferred promise which resolves when importing is done. */
done: Deferred<InitializationStats>;
/** The total number of target files to import. */
targetTotal: number;
/** The time that init started in milliseconds. */
start: number;
/** Total number of files to import. */
files: number;
/** Total number of imported files so far. */
initialized: number;
/** Total number of imported files. */
imported: number;
/** Total number of skipped files. */
skipped: number;
/** Total number of cached files. */
cached: number;
constructor(core: Datacore);
onload(): Promise<void>;
/** Promise which resolves when the initialization completes. */
finished(): Promise<InitializationStats>;
/** Cancel initialization. */
onunload(): void;
/** Poll for another task to execute from the queue. */
private runNext;
/** Process the result of an initialization and queue more runs. */
private handleResult;
/** Initialize a specific file. */
private init;
}
/**
* Local API provided to specific codeblocks when they are executing.
* @public
*/
export declare class DatacoreLocalApi {
api: DatacoreApi;
path: string;
/**
* The cache of all currently loaded scripts in this context.
* @private
* @internal
*/
private scriptCache;
constructor(api: DatacoreApi, path: string);
/** The current file path for the local API. */
currentPath(): string;
/** The full markdown file metadata for the current file. */
currentFile(): MarkdownPage;
/** Get acess to luxon functions. */
get luxon(): typeof luxon_2;
/** Get access to preact functions. */
get preact(): typeof preact_2;
/** Central Obsidian app object. */
get app(): App;
/** The internal plugin central datastructure. */
get core(): Datacore;
/**
* Asynchronously load a javascript block from the given path or link; you can either load from JS/TS/JSX/TSX files
* directly, or from codeblocks by loading from the section the codeblock is inside of. There are a few stipulations
* to loading:
* - You cannot load cyclical dependencies.
* - This is similar to vanilla js `require()`, not `import ... `. Your scripts you are requiring need to explicitly
* return the things they are exporting, like the example below. The `export` keyword does not work.
*
* ```js
* function MyElement() {
* ...
* }
*
* return { MyElement };
* ```
*/
require(path: string | Link): Promise<any>;
/** Utilities for coercing types into one specific type for easier programming. */
coerce: typeof Coerce;
/** Resolve a local or absolute path or link to an absolute path. */
resolvePath(path: string | Link, sourcePath?: string): string;
/** Try to parse the given query, returning a monadic success/failure result. */
tryParseQuery(query: string | IndexQuery): Result<IndexQuery, string>;
/** Try to parse the given query, throwing an error if it is invalid. */
parseQuery(query: string | IndexQuery): IndexQuery;
/** Create a file link pointing to the given path. */
fileLink(path: string): Link;
/** Create a link to a header with the given name. */
headerLink(path: string, header: string): Link;
/** Create a link to a block with the given path and block ID. */
blockLink(path: string, block: string): Link;
/** Try to parse the given link, throwing an error if it is invalid. */
parseLink(linktext: string): Link;
/** Try to parse a link, returning a monadic success/failure result. */
tryParseLink(linktext: string): Result<Link, string>;
/** Create a data array from a regular array. */
array<T>(input: T[] | DataArray<T>): DataArray<T>;
/** Evaluate an expression and return it's evaluated value. */
evaluate(expression: string | Expression, variables?: Record<string, Literal> | any, sourcePath?: string): Literal;
/** Evaluate an expression and return it's evaluated value, throwing an exception on failure. */
tryEvaluate(expression: string | Expression, variables?: Record<string, Literal> | any, sourcePath?: string): Result<Literal, string>;
/** Execute a textual or typed index query, returning all results. */
query(query: string | IndexQuery): Indexable[];
/** Execute a textual or typed index query, returning all results. */
tryQuery(query: string | IndexQuery): Result<Indexable[], string>;
/** Execute a textual or typed index query, returning results plus performance metadata. */
fullquery(query: string | IndexQuery): SearchResult<Indexable>;
/** Execute a textual or typed index query, returning results plus performance metadata. */
tryFullQuery(query: string | IndexQuery): Result<SearchResult<Indexable>, string>;
/** See the preact or react 'useState' hook. */
useState: typeof hooks.useState;
/** See the preact or react 'useCallback' hook. */
useCallback: typeof hooks.useCallback;
/** Se the preact or react 'useReducer' hook. */
useReducer: typeof hooks.useReducer;
/** See the preact or react 'useMemo' hook. */
useMemo: typeof hooks.useMemo;
/** See the preact or react 'useEffect' hook. */
useEffect: typeof hooks.useEffect;
/** See the preact or react 'createContext' function. */
createContext: typeof preact_2.createContext;
/** See the preact or react 'useContext' function. */
useContext: typeof hooks.useContext;
/** See the preact or react 'useRef' function. */
useRef: typeof hooks.useRef;
/**
* Calls a function to obtain a value; returns the same exact _instance_ of that value as long
* as calls to the function return an equivalent value. Interning is a useful performance concept
* for reducing the total number of unique objects in memory and for making better use of
* React's reference-equality-based caching.
*/
useInterning: typeof useInterning;
/** Memoize the input automatically and process it using a DataArray; returns a vanilla array back. */
useArray<T, U>(input: T[] | DataArray<T>, process: (data: DataArray<T>) => DataArray<U>, deps?: any[]): U[];
/** Use the file metadata for the current file. Automatically updates the view when the current file metadata changes. */
useCurrentFile(settings?: {
debounce?: number;
}): MarkdownPage;
/** Use the current path. Automatically updates the view if the path changes (though that would be weird). */
useCurrentPath(settings?: {
debounce?: number;
}): string;
/** Use the file metadata for a specific file. Automatically updates the view when the file changes. */
useFile(path: string, settings?: {
debounce?: number;
}): Indexable | undefined;
/** Automatically refresh the view whenever the index updates; returns the latest index revision ID. */
useIndexUpdates(settings?: {
debounce?: number;
}): number;
/**
* Run a query, automatically re-running it whenever the vault changes. Returns more information about the query
* execution, such as index revision and total search duration.
*/
useFullQuery(query: string | IndexQuery, settings?: {
debounce?: number;
}): SearchResult<Indexable>;
/** Run a query, automatically re-running it whenever the vault changes. */
useQuery(query: string | IndexQuery, settings?: {
debounce?: number;
}): Indexable[];
/** Vertical flexbox container; good for putting items together in a column. */
Stack: typeof Stack;
/** Horizontal flexbox container; good for putting items together in a row. */
Group: typeof Group;
/** Renders a literal value in a pretty way that respects settings. */
Literal: any;
/** Renders markdown using the Obsidian markdown renderer, optionally attaching additional styles. */
Markdown: any;
/** Renders an obsidian-style link directly and more efficiently than rendering markdown. */
Link: ({ link, sourcePath: maybeSourcePath }: {
link: string | Link;
sourcePath?: string | undefined;
}) => preact_2.JSX.Element;
/** Create a vanilla Obsidian embed for the given link. */
LinkEmbed: any;
/** Create an explicit 'span' embed which extracts a span of lines from a markdown file. */
SpanEmbed: any;
/** Renders an obsidian lucide icon. */
Icon: typeof Icon;
/**
* Generate an embed of the given markdown element. Useful to pass to the 'renderer' prop of various views
* to efficiently render embeds of various elements.
*
* For example, `dc.embed(<file>)` will produce a file embedding, and `dc.embed(<section>)` will produce a section embedding.
*/
embed: any;
/** @deprecated - Use just `Table` instead. */
VanillaTable: typeof TableView;
/** A simple and configurable table view that supports rendering paged and grouped data. */
Table: typeof TableView;
/** A simple and configurable list view that supports rendering paged and grouped data. */
List: typeof ListView;
/** A single card which can be composed into a grid view. */
Card: typeof Card;
Button: typeof Button;
Textbox: typeof Textbox;
Callout: typeof Callout;
Checkbox: typeof Checkbox;
Slider: typeof Slider;
Switch: typeof Switch;
VanillaSelect: typeof VanillaSelect;
}
/** Reactive data engine for your Obsidian.md vault. */
export declare class DatacorePlugin extends Plugin_2 {
/** Plugin-wide default settings. */
settings: Settings;
/** Central internal state. */
core: Datacore;
/** Externally visible API for querying. */
api: DatacoreApi;
onload(): Promise<void>;
onunload(): void;
/** Register codeblock highlighting and return a closure which unregisters. */
registerCodeblockHighlighting(): () => void;
/** Update the given settings to new values. */
updateSettings(settings: Partial<Settings>): Promise<void>;
}
/** Shorthand for a mapping from keys to values. */
export declare type DataObject = Record<string, any>;
/**
* Central, index storage for datacore values.
* @internal
*/
export declare class Datastore {
vault: Vault;
metadataCache: MetadataCache;
settings: Settings;
/** The current store revision. */
revision: number;
/**
* Master collection of all object IDs. This is technically redundant with objects.keys() but this is a fast set
* compared to an iterator.
*/
private ids;
/** The master collection of ALL indexed objects, mapping ID -> the object. */
private objects;
/** Map parent object to it's direct child objects. */
private children;
/** Global map of object type -> list of all objects of that type. */
private types;
/** Tracks exact tag occurence in objects. */
private etags;
/** Tracks tag occurence in objects. */
private tags;
/** Maps link strings to the object IDs that link to those links. */
private links;
/** Tracks the existence of fields (indexed by normalized key name). */
private fields;
/**
* Quick searches for objects in folders. This index only tracks top-level objects - it is expanded recursively to
* find child objects.
*/
private folder;
constructor(vault: Vault, metadataCache: MetadataCache, settings: Settings);
/** Return the total number of objects in the store. */
get size(): number;
/** Update the revision of the datastore due to an external update. */
touch(): void;
/** Load an object by ID. */
load(id: string): Indexable | undefined;
/** Load a list of objects by ID. */
load(ids: string[]): Indexable[];
/** Sets up sane field defaults for several indexable fields. */
private _initializeFields;
/**
* Store the given object, making it immediately queryable. Storing an object
* takes ownership over it, and index-specific variables (prefixed via '$') may be
* added to the object.
*/
store<T extends Indexable>(object: T | T[], substorer?: Substorer<T>): void;
/** Recursively store objects using a potential subindexer. */
private _recursiveStore;
/** Delete an object by ID from the index, recursively deleting any child objects as well. */
delete(id: string): boolean;
/** Internal method that does not bump the revision. */
private _deleteRecursive;
/** Add the given indexable to the appropriate indices. */
private _index;
/** Remove the given indexable from all indices. */
private _unindex;
/** Completely clear the datastore of all values. */
clear(): void;
/** Find the corresponding object for a given link. */
resolveLink(rawLink: string | Link, sourcePath?: string): Indexable | undefined;
/**
* Search the datastore for all documents matching the given query, returning them
* as a list of indexed objects along with performance metadata.
*/
search(query: IndexQuery, settings?: SearchSettings): Result<SearchResult<Indexable>, string>;
/** Create an expression evaluator backed by this datastore. */
evaluator(sourcePath?: string, globals?: Record<string, Literal>): Evaluator;
/** Internal search which yields a filter of results. */
private _search;
private _resolveSource;
/** Resolve leaf nodes in a search AST, yielding raw sets of results. */
private _resolvePrimitive;
/** Filter documents by field values, using the fast lookup if it returns a result and otherwise filtering over every document using the slow predicate. */
private _filterFields;
/**
* Does Breadth-first Search to find all linked files within distance <distance>. This includes all source nodes,
* so remove them afterwards if you do not want them.
*/
private _traverseLinked;
/** Iterate all linked objects for the given object. */
private _iterateAdjacentLinked;
/** Iterator which produces all parents of the given object. */
private _iterateParents;
/** Iterative which produces all children (recursively) of the given object. */
private _iterateChildren;
}
/** A promise that can be resolved directly. */
declare type Deferred<T> = Promise<T> & {
resolve: (value: T) => void;
reject: (error: any) => void;
};
/** Queues up loads of files to reduce the maximum number of concurrent loads. */
declare class EmbedQueue extends Component {
vault: Vault;
concurrency: () => number;
/** Set of pending loads. */
private queue;
/** Set of promises waiting on each path. */
private promises;
/** Active set of loads. */
private active;
/** If true, prevent any further loads. */
private shutdown;
constructor(vault: Vault, concurrency: () => number);
/** Read a file asynchronously, controlling concurrency to prevent too many files being loaded simultaneously. */
read(file: TFile): Promise<string>;
/** Schedule more loads from the queue into the active set if there is available space. */
private schedule;
/** Communicate the result of a loaded file and then schedule more files to be loaded. */
private finish;
/** Cancell all outstanding loads on unload. */
onunload(): void;
}
/**
* Evaluation context that expressions can be evaluated in. Includes global state, as well as available functions and a handler
* for binary operators.
*/
declare class Evaluator {
linkHandler: LinkHandler;
settings: Settings;
globals: Record<string, Literal>;
binaryOps: BinaryOpHandler;
functions: Record<string, FunctionImpl>;
/**
* Create a new context with the given namespace of globals, as well as optionally with custom binary operator, function,
* and link handlers.
*/
constructor(linkHandler: LinkHandler, settings: Settings, globals?: Record<string, Literal>, binaryOps?: BinaryOpHandler, functions?: Record<string, FunctionImpl>);
/** Set a global value in this context. */
set(name: string, value: Literal): Evaluator;
/** Get the value of a global variable by name. Returns null if not present. */
get(name: string): Literal;
/** Try to evaluate an arbitrary expression in this context, raising an exception on failure. */
tryEvaluate(expr: Expression, variables?: Variables): Literal;
/** Evaluate an arbitrary expression in this context. */
evaluate(expr: Expression, variables?: Variables): Result<Literal, string>;
/** General logic for making a function call. */
private evaluateFunctionCall;
}
export declare type Expression = LiteralExpression | VariableExpression | ListExpression | ObjectExpression | BinaryOpExpression | FunctionExpression | MethodExpression | LambdaExpression | NegatedExpression;
export declare namespace Expressions {
/** The implicit field referencing the current field. */
const ROW: string;
export function variable(name: string): VariableExpression;
export function literal(value: Literal): LiteralExpression;
export function binaryOp(left: Expression, op: BinaryOp, right: Expression): Expression;
export function index(obj: Expression, index: Expression): Expression;
/** Converts a string in dot-notation-format into a variable which indexes. */
export function indexVariable(name: string): Expression;
export function lambda(args: string[], value: Expression): LambdaExpression;
export function method(target: Expression, func: string, args: Expression[]): MethodExpression;
export function func(func: Expression, args: Expression[]): FunctionExpression;
export function list(values: Expression[]): ListExpression;
export function object(values: Record<string, Expression>): ObjectExpression;
export function negate(child: Expression): NegatedExpression;
export function isCompareOp(op: BinaryOp): op is CompareOp;
/** Returns a set of all unbound variables (i.e., variables not provided by `row`, lambdas, or similar.) */
export function unboundVariables(expr: Expression, bound?: Set<string>): Set<string>;
/** Render an expression as a string. */
export function toString(expr: Expression): string;
const NULL: LiteralExpression;
}
/** Quick utilities for generating fields and doing searches over them.
* @hidden
*/
export declare namespace Extractors {
/** Generate a list of fields for the given object, returning them as a list. */
export function intrinsics<T extends Record<string, any>>(except?: Set<string>): FieldExtractor<T>;
/** Field extractor which extracts frontmatter fields. */
export function frontmatter<T extends Indexable>(front: (object: T) => Record<string, FrontmatterEntry> | undefined): FieldExtractor<T>;
/** Field extractor which shows all inline fields. */
export function inlineFields<T extends Indexable>(inlineMap: (object: T) => Record<string, InlineField> | undefined): FieldExtractor<T>;
/** Merge multiple field extractors into one; if multiple extractors produce identical keys, keys from the earlier extractor will be preferred. */
export function merge<T extends Fieldbearing>(...extractors: FieldExtractor<T>[]): FieldExtractor<T>;
}
/** Functional return type for error handling.
* @hidden
*/
export declare class Failure<T, E> {
error: E;
successful: false;
constructor(error: E);
map<U>(_f: (a: T) => U): Result<U, E>;
flatMap<U>(_f: (a: T) => Result<U, E>): Result<U, E>;
mapErr<U>(f: (e: E) => U): Result<T, U>;
bimap<T2, E2>(_succ: (a: T) => T2, fail: (b: E) => E2): Result<T2, E2>;
orElse(value: T): T;
cast<U>(): Result<U, E>;
orElseThrow(message?: (e: E) => string): T;
}
/**
* General definition for a field. Provides the field key, value, as well as information on it's source and how it can be edited.
* @group Common Types
* */
export declare interface Field {
/** The canonical key name for the field (i.e., as it actually shows up in the data structure). */
key: string;
/** The value of the field. */
value: Literal;
/** The raw value of the field before parsing, if relevant. */
raw?: string;
/** If present, describes where the field came from in precise detail, allowing the field to be edited. */
provenance?: Provenance;
}
export declare interface Fieldbearing {
/** Return a list of all fields. This may be computed eagerly, so cache this value for repeated operations. */
fields: Field[];
/** Fetch a field with the given name if it is present on this object. */
field(key: string): Field | undefined;
}
/** Metadata for objects which are annotated with fields. */
export declare const FIELDBEARING_TYPE = "fields";
export declare namespace Fieldbearings {
export function isFieldbearing(object: any): object is Fieldbearing;
/** Get a key from a generic map or fieldbearing object. */
export function get(object: Fieldbearing | Record<string, Literal>, key: string): Literal | undefined;
}
/**
* Generic function which extract fields. If no argument is provided, it should return all fields; otherwise,
* it should return the field matching the given key.
*
* Keys are case-insensitive to match Obsidian standard behavior.
*/
export declare type FieldExtractor<T> = (object: T, key?: string) => Field[];
/**
* {@inheritDoc FILE_TYPE}
*/
declare interface File_2 extends Linkable {
/** The path this file exists at. */
$path: string;
/** Obsidian-provided date this page was created. */
$ctime: DateTime;
/** Obsidian-provided date this page was modified. */
$mtime: DateTime;
/** Obsidian-provided size of this page in bytes. */
$size: number;
/** The extension of the file. */
$extension: string;
}
export { File_2 as File }
/** General metadata for any file. */
export declare const FILE_TYPE = "file";
/** Multi-threaded file parser which debounces rapid file requests automatically. */
declare class FileImporter extends Component {
vault: Vault;
fileManager: FileManager;
metadataCache: MetadataCache;
workers: Map<number, PoolWorker>;
/** The next worker ID to hand out. */
nextWorkerId: number;
/** If true, the importer is now inactive and will not process further files. */
shutdown: boolean;
/** List of files which have been queued for a reload. */
queue: Queue<[TFile, Deferred<any>]>;
/** Outstanding loads indexed by path. */
outstanding: Map<string, Promise<any>>;
/** Throttle settings. */
throttle: () => ImportThrottle;
constructor(vault: Vault, fileManager: Fi