Bases Parity Audit
This note audits the current Bases implementation in this repository against two upstream references:
- Obsidian API types from
obsidian.d.tsv1.12.3 - Obsidian help pages for Bases, Views, Syntax, Functions, and Formulas
The audit covers two local surfaces:
- the shared compatibility API in
packages/api/src/lib/bases.ts - the working runtime in
packages/plugins/plugin-bases/src/
The main finding is that the repository has partial Bases coverage, but parity is split across two different implementations:
@lapis-notes/apiexports a compatibility-oriented Bases surface with several type and helper shimsplugin-basesowns the actual.baseand.basesruntime, query execution, toolbar, and built-in views
That split means the repo currently has name-level parity in some places without behavior-level parity.
Progress since the initial audit
The first parity follow-up slice has landed since this note was originally written.
Completed work:
- the shared API now carries the richer
DateValueandDurationValuehelpers that had previously lived only in the plugin-private model, including parsing, relative/date-only helpers, duration math, and millisecond conversion - the shared config and option surface now includes
formulas,summaries,groupBy, recursive filter unions, and the upstream-styleoptions?: (config) => ...callback shape plugin-basesnow consumes sharedBasesPropertyId, sharedBasesViewConfig, and shared view option types in more of the runtime surface instead of maintaining separate local copies- the function gap list is now backed by an executable plugin test in
src/bases-view/functions-parity.spec.ts, which locks in the currently supported names and makes regressions visible immediately plugin-basesnow has a dedicatedvitest.config.ts, which makes these TypeScript-only parity checks runnable without bringing up the package’s Svelte/Vite browser test path- the runtime now exposes additional file-derived metadata columns for
file.links,file.embeds,file.backlinks, andfile.properties, backed by focused tests around the shared derivation logic - the runtime now threads
groupByinto its query result model sogroupedDatais populated when a Base view config includes a grouping key, backed by focused tests around the grouping helper, and the query generator now also projects the active group key even when that property is hidden from the table order so grouped renderers do not collapse intoUngrouped - the plugin now registers a built-in
listview and includes a grouped list renderer with compact unordered-list rows built from the selected columns, reducing the built-in view gap tomap - the plugin now also registers a first-class
mapview with an explicit unsupported-state renderer, so all documented built-in layout names are present even though real map capability is still missing - the source-to-preview document normalization path now preserves first-class
cardsviews instead of dropping them during YAML reloads, so newly added card views survive mode switches - grouped cards views now keep their section headers sticky within the scrolling cards pane, matching the existing grouped list/table behavior more closely
- grouped list and cards sections can now be folded from their sticky headers using local per-view collapse state
- grouped table views can now also be folded from either the inline group row or the sticky active-group header while keeping the virtualized table body in sync
- the documented file-property list is now fully exposed in the runtime, including the explicit
file.fileself-reference field - the shared API now implements
BasesView.createFileForView()using the existing file-manager path allocation, optional frontmatter processing, and file-opening flow - the plugin toolbar now surfaces create-new-file through a direct
Newbutton, while copy/export CSV actions live in the result-limit popover backed by the samecreateFileForView()and CSV serialization helper flows - preview-mode Bases config edits now write the serialized
.basedocument back to the underlying file immediately instead of waiting on the editor’s debounced autosave path, which keeps newly added views from disappearing on quick reloads - the plugin toolbar now exposes a search toggle that opens a search panel above the active view while filtering the current result set across the displayed properties, the toolbar result count now continues to reflect the underlying query result size instead of the transient search subset, and the search input now keeps the controller query state synchronized through focus changes and scrolling in the virtualized table layout while surfacing the active match count and a clear-search action inline
- the table layout now applies base-level and per-view summary config in a footer row, backed by a focused summary-resolution helper test, and the shared width map now keeps headers and body cells aligned on first render while the trailing visible column absorbs spare horizontal room; active column resizing now keeps the virtualized header and body cells in lockstep while widths shrink or grow, the sticky header background strip now matches the rendered header height, the first row starts directly beneath that header without an extra spacer gap, the first visible row now renders a top border so that junction reads as the header’s bottom edge, table headers now use a visible full-cell hover background only for non-active columns while focused headers keep a stable active background without the old sorted-column ring treatment or the extra header focus shadow, active table searches now keep a sticky in-table query strip while the panel is closed and stay synchronized with the virtualized row body when the filtered result set shrinks or the previous scroll position would otherwise leave stale row indexes behind, and grouped table views render full-width section rows for the active group key with a sticky section label beneath the table header instead of staying flat
- list, cards, and the map fallback now also expose configured summaries through a compact summary strip
- the property editor now surfaces base-level and per-view summary selectors so configured summaries are no longer edit-only in raw
.baseJSON - the sort popover now includes a
groupBypicker with direction controls above the sort list, and the popover itself scrolls when vertical space is constrained so those controls stay contained within the panel
The remaining parity work is still substantial, but the repo now has an executable checklist and less type/config duplication between @lapis-notes/api and plugin-bases.
Current implementation
Shared API
packages/api/src/lib/bases.ts currently exports the public Bases compatibility layer used by first-party plugins and parity tests. It includes:
- core identifiers and config types such as
BasesPropertyType,BasesPropertyId,BasesConfigFile,BasesViewConfig, andBasesViewRegistration Valuewrappers such asBooleanValue,NumberValue,StringValue,DateValue,DurationValue,FileValue,ListValue, andObjectValue- query result helpers such as
BasesEntry,BasesEntryGroup,BasesQueryResult,QueryController,parsePropertyId, andtoValue
This file is useful as a compatibility surface, and it now owns more of the shared value/config behavior than it did during the initial audit. It is still not the complete source of truth for the running Bases feature, because runtime query execution and view/controller behavior remain plugin-local.
Runtime plugin
packages/plugins/plugin-bases/src/ contains the working Bases feature. Today it provides:
- a
BasesPluginthat registers.baseand.basesfiles to thebasesview type - a
BasesViewfile view that supports source and preview modes - built-in
table,cards,list,map, and fallbackunknownrenderers - a query controller that builds rows from
metadataCache, frontmatter, and a PEaQL-backed in-memory table - toolbar actions for view selection, limit, sort, group, filters, search, CSV export/copy, note creation, and property visibility/formulas/summaries
The runtime supports:
- global filters and per-view filters
- formula columns
- base-level and per-view summaries
- grouped result derivation from
groupBy - sort order
- row limits
- search across displayed properties
- frontmatter-backed note properties
- toolbar create/copy/export workflows
- a small set of file-derived columns such as
file.name,file.path,file.folder,file.ext,file.ctime,file.mtime,file.size, andfile.tags
Internal duplication
The plugin also carries a second, private Bases runtime model in packages/plugins/plugin-bases/src/bases-view/bases.svelte.ts.
Some of the duplication identified in the original audit has now been reduced:
DateValueandDurationValuehelper behavior has been moved into the shared API- shared
BasesViewConfigand shared option types are now used by the plugin’s view configuration surface
The remaining private model still owns the runtime-specific pieces, especially:
QueryController- the plugin-local
BasesViewbase class - the running query/result lifecycle
Because the plugin and shared API evolve independently, parity work done in one layer does not automatically reach the other.
Confirmed parity gaps
1. Shared API type drift from upstream
The shared API does not currently match the upstream Bases types closely enough to claim parity.
Confirmed drift includes:
BasesPropertyIdis declared asstring | ...locally, while upstream requires a prefixed form${BasesPropertyType}.${string}BasesConfigFilenow includes upstream-styleformulasandsummaries, and the runtime executes more of that config than it did initially, but not with full upstream semanticsBasesConfigFileFilteris currently a broadRecord<string, unknown>instead of the recursive upstream union ofstring | { and } | { or } | { not }BasesConfigFileViewnow includesgroupByandsummaries, and the runtime uses both fields, but grouped-view and summary behavior is still narrower than upstream- option interfaces have been expanded to cover fields such as
placeholder,filter,instant, andshouldHide, but view/config semantics are still incomplete in the runtime BasesOptionGroupandBasesViewRegistration.optionsnow match the upstream grouped-options and callback shape more closelyBasesView.createFileForView()is now implemented in the shared API and surfaced in the plugin toolbar, but the public API still does not drive the full runtime directlyBasesViewConfig.getEvaluatedFormula()is a stub that wraps raw config values instead of evaluating a formula in Bases context
2. Runtime config semantics are narrower than Obsidian Bases
The current plugin runtime only implements a subset of the documented Bases config model.
Confirmed gaps include:
- the runtime now groups
groupedDatawhengroupByis present in the view config, and the toolbar can edit that config; the list, cards, and table layouts now render grouped sections explicitly, but other grouped-view semantics are still narrower than upstream - no evidence that
thiscontext is supported the way the docs describe for opened, embedded, and sidebar Bases
The runtime query generator currently applies formulas, filters, sort, limit, grouped result derivation, and summary aggregation, but not the broader grouped-view workflows or this semantics that Obsidian exposes.
3. Built-in view coverage is incomplete
Obsidian help documents four built-in view layouts:
tablecardslistmap
The current plugin now registers:
tablecardslistmapunknown
For map, the upstream product also depends on the Maps plugin. The local runtime now provides an explicit fallback state for that layout name, but it still does not implement a real map-capable renderer.
4. Toolbar and workflow coverage is incomplete
Obsidian’s help pages document a broader toolbar and workflow surface than the current plugin provides.
Currently present:
- view selection and editing
- limit
- sort
- filter
- property visibility and formula editing
- search over displayed properties through a toggleable panel above the active view
- base-level and per-view summary editing for the selected property
- group configuration editing
Documented but currently missing or not surfaced in the plugin:
- no additional toolbar/workflow gaps are currently called out by this audit beyond the remaining layout/semantic differences below
5. File property coverage is incomplete
The docs describe a broader file-property surface than the plugin currently provides. Verified local coverage today is limited to:
filefile.filefile.namefile.basenamefile.fullnamefile.pathfile.folderfile.extfile.ctimefile.mtimefile.sizefile.tagsfile.linksfile.embedsfile.backlinksfile.properties
The documented file-property names are now exposed in the runtime. Some related helpers such as hasLink() and hasProperty() exist, but richer file-value semantics are still missing.
6. Formula function coverage is partial
Verified local function coverage comes from two places:
- explicit Bases registrations in
plugin-bases/src/bases-view/functions.ts - generic PEaQL functions in
node_modules/peaql/src/lib/query/query_env.ts
Verified function names available today:
- local Bases-specific:
isEmpty,isTruthy,toString,startsWith,endsWith,contains,containsAny,containsAll,containsAnyOf,containsAllOf,inFolder,hasLink,hasTag,hasProperty,length,map,filter,reduce,icon,title,relative,time,trim,split,replace,repeat,reverse,slice,ceil,floor,list,number,max,min,random,flat,join,sort,unique,escapeHTML,date,duration,file,html,image,link,if,isType,asFile,asLink,linksTo,keys,values,matches - PEaQL-provided names that overlap partially with Bases docs:
abs,round,toFixed,today,now,format,lower
The remaining function parity gaps are now mostly about value-model depth and method-style semantics rather than missing registered function names.
Documented method-style functions that are missing, only partially present, or exposed under different names:
- any: thin
isTruthy,toString, andisTypehelpers are now registered, but the broader method-style value API is still incomplete - date:
date(),format(),time(),relative()as date methods rather than only ad hoc helpers - string: method-style dispatch still needs broader runtime coverage beyond the newly registered names
- number: method-style dispatch still needs broader runtime coverage beyond
ceilandfloor - list: method-style dispatch semantics still need deeper runtime coverage even though the helper names are now registered
- link:
asFileandlinksToare now registered as thin helpers, but richer link-value semantics are still missing - file:
asLinkis now registered as a thin helper, but richer file-value semantics are still missing - object: broader value-model integration still needed beyond
keysandvalues - regexp: broader value-model integration still needed beyond
matches
There is also naming drift from the docs:
- docs use
containsAnyandcontainsAll - the runtime now exposes both the upstream names and the older local aliases
containsAnyOfandcontainsAllOf
That reduces one compatibility gap, but formula compatibility is still not aligned with Obsidian’s public Bases language overall.
7. Value-model parity is incomplete
The shared API exports some upstream value classes by name, but behavior is incomplete.
Examples:
- shared
DateValueandDurationValuenow expose the core helper methods that were called out in the original audit, but other value wrappers remain thin - shared
LinkValue,HTMLValue,IconValue,ImageValue,TagValue,UrlValue, andRegExpValueare still mostly marker subclasses without upstream behavior - the private plugin model still owns runtime-specific view/controller behavior that is not shared through
@lapis-notes/api
8. Parity reporting is more optimistic than the actual implementation
packages/api/scripts/generate-obsidian-parity.mjs marks many Bases symbols as implemented through explicit overrides. The generated parity fixture therefore reports name-level coverage, but it does not validate:
- type accuracy
- method completeness
- runtime semantics
- formula language compatibility
- UI workflow parity
The current parity report is useful as an inventory, but not as proof of working Bases parity.
Plan to attain parity
1. Make @lapis-notes/api the single source of truth for Bases types and values
Unify the duplicated Bases model so the plugin consumes the same public implementation that the rest of the repo imports.
Work items:
- move the richer private value and config types out of
plugin-basesand intopackages/api/src/lib/bases.ts - update
plugin-basesto import those shared types instead of maintaining a parallel copy - align all exported types and method signatures to upstream
obsidian.d.tsunless the repo intentionally documents a divergence
2. Close the shared API type gaps first
Before adding more UI behavior, the public API should match the upstream Bases contract closely.
Work items:
- correct
BasesPropertyId,BasesConfigFile,BasesConfigFileFilter,BasesConfigFileView, and option interfaces - finish aligning the remaining upstream fields and semantics such as stricter filter typing,
placeholder,filter,instant, andshouldHide - update
BasesViewRegistration.optionsto the upstream callback signature - implement
BasesViewConfig.getEvaluatedFormula()rather than leaving it as a stub
3. Implement the documented Bases value model and function surface
Formula compatibility depends on both type wrappers and function dispatch.
Work items:
- finish the value wrappers for
DateValue,DurationValue,LinkValue,HTMLValue,ImageValue,IconValue,FileValue,ListValue,ObjectValue, andRegExpValue - add the documented global and method-style Bases functions under the upstream names
- add compatibility aliases only where needed, but prefer Obsidian’s names over local variants
- explicitly map or wrap PEaQL helpers so formula authors can use Obsidian syntax instead of query-engine internals
- add tests for every documented function name and for key return-type/rendering behavior
4. Expand the query engine to match Bases semantics
The runtime currently handles formula projection, filtering, sorting, and limit. It needs the rest of the documented Bases execution model.
Work items:
- deepen grouped-view rendering beyond the current list, cards, and table grouped sections
- implement custom summary formulas beyond the current keyed aggregate support
- add
thisbinding semantics for opened, embedded, and sidebar Bases - support the documented file-property fields such as backlinks, links, embeds, and properties with clear refresh behavior
5. Bring the built-in views and toolbar up to documented coverage
Once the shared model and query runtime are aligned, the UI should match the documented Bases workflows.
Work items:
- design a
mapview strategy that can either integrate with a future map capability or fail explicitly behind capability detection - extend grouped rendering and richer grouped workflows to
cards,table, and any eventualmapimplementation
6. Replace name-level parity overrides with executable parity checks
The current parity fixture should become a starting point, not the success criterion.
Work items:
- keep the symbol inventory, but stop treating explicit overrides as proof of parity
- add shared API tests that validate signatures and observable behavior for Bases-specific classes
- add plugin-level tests that exercise
.baseparsing, formulas, filters, summaries, grouping, and view registration - add function-coverage tests that compare the documented Bases function list against the registered/effective runtime function list
Recommended delivery order
To reduce churn, parity work should land in this order:
- Continue unifying the shared API and plugin-private Bases model, starting with the remaining runtime classes instead of recreating more config/value shims locally.
- Align config and value types with upstream
obsidian.d.ts. - Add missing function names and value behaviors with exhaustive tests.
- Deepen method-style value behavior, grouped rendering,
this, and missing file semantics. - Finish the remaining built-in view behavior, especially real map capability and non-list grouped layouts.
- Add map view only after the runtime has a clear map-capability story.
- Tighten parity tooling so future audits measure behavior, not just symbol presence.
Current parity status
Current status can be summarized as follows:
- shared API parity: partial and type-shim heavy
- runtime query parity: partial but broader than the initial audit
- documented function parity: partial but largely name-complete
- built-in view parity: near-complete on layout names, incomplete on grouped behavior and map capability
- toolbar/workflow parity: broad for the documented day-to-day controls
- parity reporting fidelity: optimistic
The repository is far enough along to support basic Bases workflows, but it should not yet be considered at parity with upstream Obsidian Bases.