Skip to content

Repository files navigation

@tasknotes/model

Portable TaskNotes model semantics for JavaScript and TypeScript consumers. This package is the shared, host-independent implementation of the TaskNotes data model.

It intentionally contains no Obsidian API usage, no vault IO, no process exits, and no UI code. Hosts such as the Obsidian plugin, mdbase-tasknotes, companion plugins, or automation tools should use this package for deterministic TaskNotes behavior, then perform their own persistence and presentation.

Responsibilities

@tasknotes/model owns:

  • TaskNotes task, config, field mapping, status, priority, recurrence, and time-entry types
  • default model configuration
  • TaskNotes frontmatter mapping and normalization
  • attachment-list normalization, safe collection-path resolution, canonical references, and validation
  • date parsing, date comparison, and storage-date semantics
  • recurrence evaluation and schedule advancement
  • materialized occurrence identity, creation, completion, skip, and parent reconciliation plans
  • time tracking entry planning and duration calculation
  • validation helpers
  • host-independent operation plans for common task mutations
  • spec-normalized adapter helpers for CLI and mdbase-style consumers
  • tasknotes-spec conformance operation helpers
  • canonical mdbase record and event contract artifacts

Hosts own:

  • file, vault, database, or network IO
  • Obsidian app APIs and metadata cache reads
  • command registration and UI state
  • notifications, logging, process exits, and error presentation
  • sync provider lifecycles
  • path resolution against host-specific collection or vault rules

Module Map

The package exports both the root module and focused subpath modules:

ModulePurpose
@tasknotes/modelMain barrel export for all public APIs
@tasknotes/model/typesShared TaskNotes model and operation types
@tasknotes/model/defaultsDefault field mapping, statuses, priorities, and model config
@tasknotes/model/configModel config resolution and tasknotes-spec field mapping helpers
@tasknotes/model/dateDate parsing, validation, comparison, and storage formatting
@tasknotes/model/attachmentsCanonical attachment references plus safe collection-path normalization and validation
@tasknotes/model/mappingTaskNotes frontmatter mapping, dependency mapping, and value normalization
@tasknotes/model/schemaZod schemas for model validation
@tasknotes/model/recurrenceRecurrence evaluation, DTSTART handling, and schedule recalculation
@tasknotes/model/timeTime-entry sanitizing, timer plans, and duration totals
@tasknotes/model/validationTask and time-entry validation
@tasknotes/model/operationsHost-independent task mutation planning, including materialized occurrence plans
@tasknotes/model/frontmatterMarkdown task document parse/serialize helpers
@tasknotes/model/mdbaseCanonical mdbase v0.3 config, contract, schema, and implementing-type generation
@tasknotes/model/conformancetasknotes-spec conformance operation dispatcher

Examples

Map TaskNotes frontmatter into normalized task data:

import{DEFAULT_FIELD_MAPPING,mapTaskFromFrontmatter}from"@tasknotes/model";consttask=mapTaskFromFrontmatter(DEFAULT_FIELD_MAPPING,{title: "Ship package",status: "open",priority: "normal",scheduled: "2026-06-01",},"Tasks/Ship package.md",false,[]);

Normalize host-independent attachment membership without storing binary metadata in frontmatter:

import{attachmentPathFromReference,canonicalAttachmentReference,validateAttachmentReferences,}from"@tasknotes/model/attachments";constreference=canonicalAttachmentReference("Attachments/receipt.jpg");// [[Attachments/receipt.jpg]]constpath=attachmentPathFromReference("[receipt](../Attachments/receipt.jpg)","Tasks/Expense report.md");// Attachments/receipt.jpgconstvalidation=validateAttachmentReferences([reference]);// { valid: true, issues: [] }

Advance a recurring task without doing any host IO:

import{completeRecurringTask}from"@tasknotes/model/recurrence";constresult=completeRecurringTask({recurrence: "FREQ=DAILY",scheduled: "2026-06-01",completionDate: "2026-06-01",completeInstances: [],skippedInstances: [],});// Host decides how to persist result.updatedRecurrence,// result.nextScheduled, result.completeInstances, and result.skippedInstances.

Build a spec-normalized adapter update for a CLI or mdbase-style surface:

import{buildSpecCompleteTaskUpdate}from"@tasknotes/model/operations";constplan=buildSpecCompleteTaskUpdate({frontmatter: {title: "Daily check",status: "open",priority: "normal",recurrence: "FREQ=DAILY",scheduled: "2026-06-01",completeInstances: [],skippedInstances: [],},targetDate: "2026-06-01",completedStatus: "done",currentTimestamp: newDate().toISOString(),});// plan.fields is spec-normalized. A host can denormalize it to its own field names// before writing to a file, database, or collection.

Materialize and reconcile a recurring occurrence:

import{buildMaterializeOccurrencePlan,buildMaterializedOccurrenceCompletePlan,}from"@tasknotes/model/operations";constmaterialized=buildMaterializeOccurrencePlan({parentTask: {title: "Weekly review",status: "open",priority: "normal",path: "Tasks/Weekly review.md",archived: false,recurrence: "DTSTART:20260601;FREQ=WEEKLY;BYDAY=MO",occurrence_materialization: "on_completion",scheduled: "2026-06-01T09:30:00",timeEstimate: 45,},targetDate: "2026-06-01",currentTimestamp: "2026-05-31T12:00:00Z",templateTask: {contexts: ["work"],},});// Host creates materialized.occurrenceTask if materialized.created is true.// Occurrence tasks inherit parent planning fields such as scheduled time,// tags/projects/contexts, reminders, details, and time estimate, but not// parent recurrence, complete/skipped instance history, or time entries.constcomplete=buildMaterializedOccurrenceCompletePlan({occurrenceTask: {
...materialized.occurrenceTask,path: "Tasks/Weekly review 2026-06-01.md",archived: false,},parentTask: materialized.parentTask,completedStatus: "done",currentTimestamp: "2026-06-01T17:00:00Z",maintainDueDateOffsetInRecurring: true,});// Host applies complete.occurrenceUpdates to the occurrence note and// complete.parentUpdates to the recurring parent. If complete.materializeNextDate// is present, the host can call buildMaterializeOccurrencePlan again for that date.

Start and stop time tracking:

import{buildStartTimeTrackingPlan,buildStopTimeTrackingPlan,getActiveTimeEntry,}from"@tasknotes/model/time";conststart=buildStartTimeTrackingPlan(task,"2026-06-01T09:00:00Z");constactive=getActiveTimeEntry(start.updatedTask);if(active){conststop=buildStopTimeTrackingPlan(start.updatedTask,active,"2026-06-01T09:30:00Z");}

Development

From this package repository:

npm run build
npm test

The package build emits ESM, CommonJS, and TypeScript declaration output under dist. Release tooling should build it before packing or publishing.

The conformance adapter reports its claimed TaskNotes profile and implements the official core-lite operation surface. Its fixture run currently passes 4,974 cases with zero failures; the extended-profile fixture remains outside that claim. The mdbase generator emits the tasknotes.task 0.3.0-rc.1 record contract, its two JSON Schemas, and a type whose implements entry contains field mappings and TaskNotes behavior. Round-trip tests resolve those resources back into model configuration so filesystem-backed hosts can treat the type as a configuration provider.

The Obsidian plugin uses this package through its service layer and keeps runtime-only behavior, such as Obsidian vault writes, metadata-cache link resolution, notices, and plugin-specific clock hooks, outside the model package.

About

TaskNotes model, mapping, validation, recurrence, and operation-planning reference implementation.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages