things-api 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -11
- package/dist/audit/log.js +56 -2
- package/dist/audit/log.js.map +1 -1
- package/dist/audit/schema.d.ts +26 -8
- package/dist/audit/schema.js +42 -1
- package/dist/audit/schema.js.map +1 -1
- package/dist/cli/commands/area.d.ts +34 -2
- package/dist/cli/commands/area.js +140 -28
- package/dist/cli/commands/area.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +1 -1
- package/dist/cli/commands/doctor.js +44 -5
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/mcp.js +23 -4
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/project.d.ts +23 -1
- package/dist/cli/commands/project.js +82 -15
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/reads.d.ts +6 -125
- package/dist/cli/commands/reads.js +649 -657
- package/dist/cli/commands/reads.js.map +1 -1
- package/dist/cli/commands/repeat-flags.d.ts +20 -0
- package/dist/cli/commands/repeat-flags.js +76 -0
- package/dist/cli/commands/repeat-flags.js.map +1 -0
- package/dist/cli/commands/setup.js +2 -3
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/show.d.ts +1 -1
- package/dist/cli/commands/show.js +169 -18
- package/dist/cli/commands/show.js.map +1 -1
- package/dist/cli/commands/snapshot.js +1 -1
- package/dist/cli/commands/snapshot.js.map +1 -1
- package/dist/cli/commands/todo.d.ts +1 -1
- package/dist/cli/commands/todo.js +21 -9
- package/dist/cli/commands/todo.js.map +1 -1
- package/dist/cli/commands/writes.js +378 -148
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/cli/did-you-mean.d.ts +29 -0
- package/dist/cli/did-you-mean.js +53 -0
- package/dist/cli/did-you-mean.js.map +1 -0
- package/dist/cli/excess-args.d.ts +15 -0
- package/dist/cli/excess-args.js +51 -0
- package/dist/cli/excess-args.js.map +1 -0
- package/dist/cli/glyphs.d.ts +67 -7
- package/dist/cli/glyphs.js +202 -19
- package/dist/cli/glyphs.js.map +1 -1
- package/dist/cli/help.d.ts +54 -0
- package/dist/cli/help.js +355 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/main.js +36 -21
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/period.d.ts +39 -0
- package/dist/cli/period.js +141 -0
- package/dist/cli/period.js.map +1 -0
- package/dist/cli/read-driver.d.ts +83 -0
- package/dist/cli/read-driver.js +215 -0
- package/dist/cli/read-driver.js.map +1 -0
- package/dist/cli/render.d.ts +246 -0
- package/dist/cli/render.js +806 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/cli/resolve-invocation.d.ts +116 -0
- package/dist/cli/resolve-invocation.js +261 -0
- package/dist/cli/resolve-invocation.js.map +1 -0
- package/dist/cli/shell-quote.d.ts +9 -0
- package/dist/cli/shell-quote.js +12 -0
- package/dist/cli/shell-quote.js.map +1 -0
- package/dist/cli/tag-filters.d.ts +59 -0
- package/dist/cli/tag-filters.js +57 -0
- package/dist/cli/tag-filters.js.map +1 -0
- package/dist/cli/verb-hint.d.ts +25 -0
- package/dist/cli/verb-hint.js +138 -0
- package/dist/cli/verb-hint.js.map +1 -0
- package/dist/cli/width.d.ts +135 -0
- package/dist/cli/width.js +313 -0
- package/dist/cli/width.js.map +1 -0
- package/dist/client.d.ts +146 -24
- package/dist/client.js +106 -16
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/contracts.d.ts +136 -1
- package/dist/contracts.js +44 -1
- package/dist/contracts.js.map +1 -1
- package/dist/db/fingerprint.d.ts +12 -0
- package/dist/db/fingerprint.js +15 -1
- package/dist/db/fingerprint.js.map +1 -1
- package/dist/db/locate.js +1 -1
- package/dist/db/locate.js.map +1 -1
- package/dist/diagnose.d.ts +63 -0
- package/dist/diagnose.js +38 -1
- package/dist/diagnose.js.map +1 -1
- package/dist/index.d.ts +30 -4
- package/dist/index.js +35 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +9 -1
- package/dist/mcp/server.js +850 -128
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/entities.d.ts +40 -7
- package/dist/model/entities.js.map +1 -1
- package/dist/model/mappers.d.ts +7 -0
- package/dist/model/mappers.js +20 -3
- package/dist/model/mappers.js.map +1 -1
- package/dist/model/occurrences.d.ts +6 -1
- package/dist/model/occurrences.js +9 -5
- package/dist/model/occurrences.js.map +1 -1
- package/dist/model/recurrence.d.ts +17 -8
- package/dist/model/recurrence.js.map +1 -1
- package/dist/model/serialize.d.ts +28 -0
- package/dist/model/serialize.js +80 -0
- package/dist/model/serialize.js.map +1 -0
- package/dist/model/when-sugar.d.ts +47 -0
- package/dist/model/when-sugar.js +45 -0
- package/dist/model/when-sugar.js.map +1 -0
- package/dist/read/area-view.d.ts +8 -1
- package/dist/read/area-view.js +49 -13
- package/dist/read/area-view.js.map +1 -1
- package/dist/read/caps.d.ts +14 -0
- package/dist/read/caps.js +21 -0
- package/dist/read/caps.js.map +1 -0
- package/dist/read/filter-contract.d.ts +117 -0
- package/dist/read/filter-contract.js +78 -0
- package/dist/read/filter-contract.js.map +1 -0
- package/dist/read/log-boundary.d.ts +9 -4
- package/dist/read/log-boundary.js.map +1 -1
- package/dist/read/predicates.d.ts +61 -0
- package/dist/read/predicates.js +78 -0
- package/dist/read/predicates.js.map +1 -0
- package/dist/read/project-view.d.ts +12 -1
- package/dist/read/project-view.js +39 -8
- package/dist/read/project-view.js.map +1 -1
- package/dist/read/queries.d.ts +156 -8
- package/dist/read/queries.js +261 -38
- package/dist/read/queries.js.map +1 -1
- package/dist/read/search-rank.d.ts +36 -0
- package/dist/read/search-rank.js +31 -0
- package/dist/read/search-rank.js.map +1 -0
- package/dist/read/sections.d.ts +51 -0
- package/dist/read/sections.js +37 -0
- package/dist/read/sections.js.map +1 -0
- package/dist/read/show-target.d.ts +13 -4
- package/dist/read/show-target.js +11 -2
- package/dist/read/show-target.js.map +1 -1
- package/dist/read/sidebar-order.d.ts +19 -0
- package/dist/read/sidebar-order.js +93 -0
- package/dist/read/sidebar-order.js.map +1 -0
- package/dist/read/tags.d.ts +27 -3
- package/dist/read/tags.js +83 -12
- package/dist/read/tags.js.map +1 -1
- package/dist/read/truncation.d.ts +71 -0
- package/dist/read/truncation.js +217 -0
- package/dist/read/truncation.js.map +1 -0
- package/dist/read/views.d.ts +177 -19
- package/dist/read/views.js +390 -213
- package/dist/read/views.js.map +1 -1
- package/dist/surface-copy.d.ts +42 -0
- package/dist/surface-copy.js +55 -0
- package/dist/surface-copy.js.map +1 -1
- package/dist/sync-health.d.ts +78 -0
- package/dist/sync-health.js +312 -0
- package/dist/sync-health.js.map +1 -0
- package/dist/write/accessibility-probe.d.ts +12 -0
- package/dist/write/accessibility-probe.js +63 -0
- package/dist/write/accessibility-probe.js.map +1 -0
- package/dist/write/automation-probe.d.ts +7 -0
- package/dist/write/automation-probe.js +8 -1
- package/dist/write/automation-probe.js.map +1 -1
- package/dist/write/batch.js +4 -2
- package/dist/write/batch.js.map +1 -1
- package/dist/write/capabilities.d.ts +8 -0
- package/dist/write/capabilities.js +13 -5
- package/dist/write/capabilities.js.map +1 -1
- package/dist/write/commands.d.ts +2 -0
- package/dist/write/commands.js +562 -48
- package/dist/write/commands.js.map +1 -1
- package/dist/write/edit-checklist.js +3 -2
- package/dist/write/edit-checklist.js.map +1 -1
- package/dist/write/guards.d.ts +2 -7
- package/dist/write/guards.js +64 -6
- package/dist/write/guards.js.map +1 -1
- package/dist/write/heading.js +2 -0
- package/dist/write/heading.js.map +1 -1
- package/dist/write/lock.d.ts +40 -2
- package/dist/write/lock.js +91 -14
- package/dist/write/lock.js.map +1 -1
- package/dist/write/make-repeating-project.d.ts +4 -0
- package/dist/write/make-repeating-project.js +253 -0
- package/dist/write/make-repeating-project.js.map +1 -0
- package/dist/write/operations.d.ts +145 -1
- package/dist/write/operations.js +48 -0
- package/dist/write/operations.js.map +1 -1
- package/dist/write/pipeline.d.ts +19 -0
- package/dist/write/pipeline.js +109 -26
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/pre-state.d.ts +68 -3
- package/dist/write/pre-state.js +102 -3
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/reopen.js +3 -2
- package/dist/write/reopen.js.map +1 -1
- package/dist/write/reorder.js +27 -17
- package/dist/write/reorder.js.map +1 -1
- package/dist/write/repeat-rule.d.ts +22 -0
- package/dist/write/repeat-rule.js +258 -0
- package/dist/write/repeat-rule.js.map +1 -0
- package/dist/write/reversibility.js +49 -0
- package/dist/write/reversibility.js.map +1 -1
- package/dist/write/tag-refs.d.ts +47 -0
- package/dist/write/tag-refs.js +126 -0
- package/dist/write/tag-refs.js.map +1 -0
- package/dist/write/undo.d.ts +74 -5
- package/dist/write/undo.js +494 -80
- package/dist/write/undo.js.map +1 -1
- package/dist/write/vectors/registry.d.ts +17 -1
- package/dist/write/vectors/registry.js +18 -2
- package/dist/write/vectors/registry.js.map +1 -1
- package/dist/write/vectors/types.d.ts +119 -3
- package/dist/write/vectors/ui-certification.d.ts +48 -0
- package/dist/write/vectors/ui-certification.js +46 -0
- package/dist/write/vectors/ui-certification.js.map +1 -0
- package/dist/write/vectors/ui-drag.d.ts +188 -0
- package/dist/write/vectors/ui-drag.js +1095 -0
- package/dist/write/vectors/ui-drag.js.map +1 -0
- package/dist/write/vectors/ui-recipes.d.ts +70 -0
- package/dist/write/vectors/ui-recipes.js +558 -0
- package/dist/write/vectors/ui-recipes.js.map +1 -0
- package/dist/write/vectors/ui.d.ts +137 -0
- package/dist/write/vectors/ui.js +656 -0
- package/dist/write/vectors/ui.js.map +1 -0
- package/dist/write/verify/delta.d.ts +31 -3
- package/dist/write/verify/delta.js +22 -6
- package/dist/write/verify/delta.js.map +1 -1
- package/dist/write/verify/poller.js +1 -0
- package/dist/write/verify/poller.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/exit-codes.d.ts +0 -26
- package/dist/cli/exit-codes.js +0 -26
- package/dist/cli/exit-codes.js.map +0 -1
- package/dist/cli/output.d.ts +0 -42
- package/dist/cli/output.js +0 -16
- package/dist/cli/output.js.map +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** Real to-dos/projects (type IN 0,1), untrashed, excluding repeating templates. */
|
|
2
|
+
export declare const LIVE = "t.type IN (0, 1) AND t.trashed = 0 AND (t.rt1_recurrenceRule IS NULL AND t.repeater IS NULL)";
|
|
3
|
+
/** {@link LIVE} restricted to open (status = 0) rows. */
|
|
4
|
+
export declare const OPEN = "t.type IN (0, 1) AND t.trashed = 0 AND (t.rt1_recurrenceRule IS NULL AND t.repeater IS NULL) AND t.status = 0";
|
|
5
|
+
/**
|
|
6
|
+
* The GUI-visible set: open rows PLUS closed (completed/canceled) rows the
|
|
7
|
+
* periodic log-move sweep has NOT yet carried to the Logbook — completion ≠
|
|
8
|
+
* logged (src/read/log-boundary.ts). A closed row keeps its slot in its list
|
|
9
|
+
* until the sweep passes it, exactly as the GUI keeps it checked in place; it
|
|
10
|
+
* leaves when the sweep boundary advances past its stopDate (boundary-relative,
|
|
11
|
+
* no new state). This is {@link LIVE} widened to that set: swept iff
|
|
12
|
+
* status IN (2,3) AND stopDate IS NOT NULL AND stopDate <= boundary — the
|
|
13
|
+
* negation of the {@link markLogged} `logged` predicate, so an included closed
|
|
14
|
+
* row always materializes with `logged=false`. Mirrors the project/area card
|
|
15
|
+
* precedent (project-view.ts / area-view.ts keep a closed-but-unswept row
|
|
16
|
+
* checked in place). ONE bind: the log boundary as epoch seconds
|
|
17
|
+
* (logBoundary(db, now).getTime() / 1000). Ruling 2026-07-14 (Mike): GUI parity
|
|
18
|
+
* — `today` and `anytime` show checked-but-unswept rows.
|
|
19
|
+
*/
|
|
20
|
+
export declare const OPEN_OR_UNSWEPT = "t.type IN (0, 1) AND t.trashed = 0 AND (t.rt1_recurrenceRule IS NULL AND t.repeater IS NULL) AND NOT (t.status IN (2, 3) AND t.stopDate IS NOT NULL AND t.stopDate <= ?)";
|
|
21
|
+
/**
|
|
22
|
+
* The `--overdue` content scope: OPEN items whose deadline falls strictly
|
|
23
|
+
* BEFORE today. Due-TODAY is deliberately NOT overdue (`<`, not `<=`) —
|
|
24
|
+
* mirroring the app's own Today badge split, where a deadline EQUAL to today
|
|
25
|
+
* reads as "due" and only an EARLIER deadline reads as "overdue". One bind:
|
|
26
|
+
* today as a packed-date int (encodePackedDate(localToday(now))), so the
|
|
27
|
+
* boundary rides the same injected clock every other view uses — never a
|
|
28
|
+
* hardcoded date. The `t.status = 0` clause re-tightens the OPEN_OR_UNSWEPT
|
|
29
|
+
* views (today/anytime) so a checked-but-unswept row that happens to sit past
|
|
30
|
+
* a deadline is excluded — overdue is remaining, OPEN work. Trash exclusion is
|
|
31
|
+
* the hosting view's job (every view this composes into already drops trashed
|
|
32
|
+
* and derived-trashed rows), so it is not repeated here.
|
|
33
|
+
*/
|
|
34
|
+
export declare const OVERDUE = "t.deadline IS NOT NULL AND t.deadline < ? AND t.status = 0";
|
|
35
|
+
/** An item's own anytime membership: unscheduled-active, or dated <= today. */
|
|
36
|
+
export declare const ANYTIME_SELF: (col: string) => string;
|
|
37
|
+
/**
|
|
38
|
+
* The item's effective project: its own link, or its heading's project for
|
|
39
|
+
* headed children (heading rows carry the project link).
|
|
40
|
+
*/
|
|
41
|
+
export declare const EFF_PROJECT = "COALESCE(t.project, (SELECT h.project FROM TMTask h WHERE h.uuid = t.heading))";
|
|
42
|
+
/**
|
|
43
|
+
* Container cascade (live-verified against the UI, 2026-07-09): a to-do
|
|
44
|
+
* inside a project that is NOT itself anytime-visible (someday or
|
|
45
|
+
* future-scheduled, logged, or trashed) is absent from Anytime regardless of
|
|
46
|
+
* the to-do's own start state — the project row alone represents it.
|
|
47
|
+
* Projects and container-less to-dos pass through. Two binds (packedToday ×2).
|
|
48
|
+
*/
|
|
49
|
+
export declare const PROJECT_ANYTIME_ACTIVE: string;
|
|
50
|
+
/**
|
|
51
|
+
* DERIVED-trash exclusion: project deletion is SHALLOW (A24B — only the
|
|
52
|
+
* project row flips trashed=1; children keep trashed=0 and their links, so
|
|
53
|
+
* their Trash membership is derived through the container chain). Every live
|
|
54
|
+
* view must therefore check the chain, not just the row's own flag: the
|
|
55
|
+
* heading (if any) and the effective project (direct or via heading) must
|
|
56
|
+
* both be untrashed. Areas cannot be trashed (they delete permanently), so
|
|
57
|
+
* the chain is at most heading → project. Trash-adjacent surfaces stay
|
|
58
|
+
* exempt on purpose: `things trash` lists directly-flagged rows, and a
|
|
59
|
+
* trashed project's OWN view shows its would-be-recovered children.
|
|
60
|
+
*/
|
|
61
|
+
export declare const CONTAINER_UNTRASHED = "(t.heading IS NULL OR EXISTS (\n SELECT 1 FROM TMTask hh WHERE hh.uuid = t.heading AND hh.trashed = 0))\n AND (COALESCE(t.project, (SELECT h.project FROM TMTask h WHERE h.uuid = t.heading)) IS NULL OR EXISTS (\n SELECT 1 FROM TMTask cc WHERE cc.uuid = COALESCE(t.project, (SELECT h.project FROM TMTask h WHERE h.uuid = t.heading)) AND cc.trashed = 0))";
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared SQL predicate fragments for the list views (src/read/views.ts).
|
|
3
|
+
* Every live view composes these so the membership rules — untrashed, open,
|
|
4
|
+
* anytime-self, the effective-project resolution, and the derived-trash
|
|
5
|
+
* container checks — are written once and cannot drift between views.
|
|
6
|
+
*
|
|
7
|
+
* Derivation and live-probe provenance are documented at each view's call
|
|
8
|
+
* site in views.ts and in docs/atlas/schema-v26.md.
|
|
9
|
+
*/
|
|
10
|
+
import { NOT_TEMPLATE } from "./queries.js";
|
|
11
|
+
/** Real to-dos/projects (type IN 0,1), untrashed, excluding repeating templates. */
|
|
12
|
+
export const LIVE = `t.type IN (0, 1) AND t.trashed = 0 AND ${NOT_TEMPLATE}`;
|
|
13
|
+
/** {@link LIVE} restricted to open (status = 0) rows. */
|
|
14
|
+
export const OPEN = `${LIVE} AND t.status = 0`;
|
|
15
|
+
/**
|
|
16
|
+
* The GUI-visible set: open rows PLUS closed (completed/canceled) rows the
|
|
17
|
+
* periodic log-move sweep has NOT yet carried to the Logbook — completion ≠
|
|
18
|
+
* logged (src/read/log-boundary.ts). A closed row keeps its slot in its list
|
|
19
|
+
* until the sweep passes it, exactly as the GUI keeps it checked in place; it
|
|
20
|
+
* leaves when the sweep boundary advances past its stopDate (boundary-relative,
|
|
21
|
+
* no new state). This is {@link LIVE} widened to that set: swept iff
|
|
22
|
+
* status IN (2,3) AND stopDate IS NOT NULL AND stopDate <= boundary — the
|
|
23
|
+
* negation of the {@link markLogged} `logged` predicate, so an included closed
|
|
24
|
+
* row always materializes with `logged=false`. Mirrors the project/area card
|
|
25
|
+
* precedent (project-view.ts / area-view.ts keep a closed-but-unswept row
|
|
26
|
+
* checked in place). ONE bind: the log boundary as epoch seconds
|
|
27
|
+
* (logBoundary(db, now).getTime() / 1000). Ruling 2026-07-14 (Mike): GUI parity
|
|
28
|
+
* — `today` and `anytime` show checked-but-unswept rows.
|
|
29
|
+
*/
|
|
30
|
+
export const OPEN_OR_UNSWEPT = `${LIVE} AND NOT (t.status IN (2, 3) AND t.stopDate IS NOT NULL AND t.stopDate <= ?)`;
|
|
31
|
+
/**
|
|
32
|
+
* The `--overdue` content scope: OPEN items whose deadline falls strictly
|
|
33
|
+
* BEFORE today. Due-TODAY is deliberately NOT overdue (`<`, not `<=`) —
|
|
34
|
+
* mirroring the app's own Today badge split, where a deadline EQUAL to today
|
|
35
|
+
* reads as "due" and only an EARLIER deadline reads as "overdue". One bind:
|
|
36
|
+
* today as a packed-date int (encodePackedDate(localToday(now))), so the
|
|
37
|
+
* boundary rides the same injected clock every other view uses — never a
|
|
38
|
+
* hardcoded date. The `t.status = 0` clause re-tightens the OPEN_OR_UNSWEPT
|
|
39
|
+
* views (today/anytime) so a checked-but-unswept row that happens to sit past
|
|
40
|
+
* a deadline is excluded — overdue is remaining, OPEN work. Trash exclusion is
|
|
41
|
+
* the hosting view's job (every view this composes into already drops trashed
|
|
42
|
+
* and derived-trashed rows), so it is not repeated here.
|
|
43
|
+
*/
|
|
44
|
+
export const OVERDUE = `t.deadline IS NOT NULL AND t.deadline < ? AND t.status = 0`;
|
|
45
|
+
/** An item's own anytime membership: unscheduled-active, or dated <= today. */
|
|
46
|
+
export const ANYTIME_SELF = (col) => `((${col}.start = 1 AND (${col}.startDate IS NULL OR ${col}.startDate <= ?))
|
|
47
|
+
OR (${col}.start = 2 AND ${col}.startDate IS NOT NULL AND ${col}.startDate <= ?))`;
|
|
48
|
+
/**
|
|
49
|
+
* The item's effective project: its own link, or its heading's project for
|
|
50
|
+
* headed children (heading rows carry the project link).
|
|
51
|
+
*/
|
|
52
|
+
export const EFF_PROJECT = `COALESCE(t.project, (SELECT h.project FROM TMTask h WHERE h.uuid = t.heading))`;
|
|
53
|
+
/**
|
|
54
|
+
* Container cascade (live-verified against the UI, 2026-07-09): a to-do
|
|
55
|
+
* inside a project that is NOT itself anytime-visible (someday or
|
|
56
|
+
* future-scheduled, logged, or trashed) is absent from Anytime regardless of
|
|
57
|
+
* the to-do's own start state — the project row alone represents it.
|
|
58
|
+
* Projects and container-less to-dos pass through. Two binds (packedToday ×2).
|
|
59
|
+
*/
|
|
60
|
+
export const PROJECT_ANYTIME_ACTIVE = `(${EFF_PROJECT} IS NULL OR EXISTS (
|
|
61
|
+
SELECT 1 FROM TMTask p WHERE p.uuid = ${EFF_PROJECT}
|
|
62
|
+
AND p.trashed = 0 AND p.status = 0 AND ${ANYTIME_SELF("p")}))`;
|
|
63
|
+
/**
|
|
64
|
+
* DERIVED-trash exclusion: project deletion is SHALLOW (A24B — only the
|
|
65
|
+
* project row flips trashed=1; children keep trashed=0 and their links, so
|
|
66
|
+
* their Trash membership is derived through the container chain). Every live
|
|
67
|
+
* view must therefore check the chain, not just the row's own flag: the
|
|
68
|
+
* heading (if any) and the effective project (direct or via heading) must
|
|
69
|
+
* both be untrashed. Areas cannot be trashed (they delete permanently), so
|
|
70
|
+
* the chain is at most heading → project. Trash-adjacent surfaces stay
|
|
71
|
+
* exempt on purpose: `things trash` lists directly-flagged rows, and a
|
|
72
|
+
* trashed project's OWN view shows its would-be-recovered children.
|
|
73
|
+
*/
|
|
74
|
+
export const CONTAINER_UNTRASHED = `(t.heading IS NULL OR EXISTS (
|
|
75
|
+
SELECT 1 FROM TMTask hh WHERE hh.uuid = t.heading AND hh.trashed = 0))
|
|
76
|
+
AND (${EFF_PROJECT} IS NULL OR EXISTS (
|
|
77
|
+
SELECT 1 FROM TMTask cc WHERE cc.uuid = ${EFF_PROJECT} AND cc.trashed = 0))`;
|
|
78
|
+
//# sourceMappingURL=predicates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predicates.js","sourceRoot":"","sources":["../../src/read/predicates.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,oFAAoF;AACpF,MAAM,CAAC,MAAM,IAAI,GAAG,0CAA0C,YAAY,EAAE,CAAC;AAE7E,yDAAyD;AACzD,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,IAAI,mBAAmB,CAAC;AAE/C;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,IAAI,8EAA8E,CAAC;AAErH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,4DAA4D,CAAC;AAEpF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAW,EAAU,EAAE,CAClD,KAAK,GAAG,mBAAmB,GAAG,yBAAyB,GAAG;UAClD,GAAG,kBAAkB,GAAG,8BAA8B,GAAG,mBAAmB,CAAC;AAEvF;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,gFAAgF,CAAC;AAE5G;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,WAAW;6CACR,WAAW;8CACV,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;QAE3B,WAAW;+CAC4B,WAAW,uBAAuB,CAAC"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { DatabaseSync } from "node:sqlite";
|
|
11
11
|
import type { Heading, IsoDateGroup, Project, Todo } from "../model/entities.ts";
|
|
12
|
+
import { type ViewFilter } from "./views.ts";
|
|
12
13
|
export interface ProjectView {
|
|
13
14
|
project: Project;
|
|
14
15
|
/** Open, unscheduled/current children not under a heading, by index. */
|
|
@@ -27,8 +28,18 @@ export interface ProjectView {
|
|
|
27
28
|
};
|
|
28
29
|
logged: Todo[];
|
|
29
30
|
trashed: Todo[];
|
|
31
|
+
/**
|
|
32
|
+
* PLOG1 (additive): count of untrashed OPEN (status=0) children this project
|
|
33
|
+
* still holds while it is ITSELF completed or canceled — including once swept
|
|
34
|
+
* to the Logbook. Such items are invisible in every live app view (Today /
|
|
35
|
+
* Anytime / Inbox / Upcoming), reachable only by drilling into this card
|
|
36
|
+
* (the app's GUI "Put Back" into a completed parent can strand them —
|
|
37
|
+
* docs/lab/plog1-research.md). 0 when the project is open, or holds no such
|
|
38
|
+
* child.
|
|
39
|
+
*/
|
|
40
|
+
openChildrenWhileResolved: number;
|
|
30
41
|
}
|
|
31
42
|
export declare class ProjectNotFoundError extends Error {
|
|
32
43
|
constructor(uuid: string);
|
|
33
44
|
}
|
|
34
|
-
export declare function projectView(db: DatabaseSync, uuid: string, now?: Date): ProjectView;
|
|
45
|
+
export declare function projectView(db: DatabaseSync, uuid: string, now?: Date, filter?: ViewFilter): ProjectView;
|
|
@@ -2,14 +2,17 @@ import { encodePackedDate, localToday, decodePackedDate } from "../model/dates.j
|
|
|
2
2
|
import { mapHeading, mapProject, mapTodo } from "../model/mappers.js";
|
|
3
3
|
import { fetchTagsForTasks, fetchTaskByUuid, fetchTaskRows, makeRefResolver } from "./queries.js";
|
|
4
4
|
import { logBoundary, markLogged } from "./log-boundary.js";
|
|
5
|
+
import { OVERDUE } from "./predicates.js";
|
|
5
6
|
import { inheritedTagsFor } from "./tags.js";
|
|
7
|
+
import { tagFilter } from "./views.js";
|
|
6
8
|
export class ProjectNotFoundError extends Error {
|
|
7
9
|
constructor(uuid) {
|
|
8
10
|
super(`no project with uuid ${uuid}`);
|
|
9
11
|
this.name = "ProjectNotFoundError";
|
|
10
12
|
}
|
|
11
13
|
}
|
|
12
|
-
export function projectView(db, uuid, now) {
|
|
14
|
+
export function projectView(db, uuid, now, filter = {}) {
|
|
15
|
+
const overdue = filter.overdue === true;
|
|
13
16
|
const projectRow = fetchTaskByUuid(db, uuid);
|
|
14
17
|
if (!projectRow || projectRow.type !== 1)
|
|
15
18
|
throw new ProjectNotFoundError(uuid);
|
|
@@ -21,10 +24,26 @@ export function projectView(db, uuid, now) {
|
|
|
21
24
|
project.inheritedTags = inheritedTagsFor(db, projectRow);
|
|
22
25
|
const headingRows = fetchTaskRows(db, `t.type = 2 AND t.project = ? AND t.trashed = 0 ORDER BY t."index" ASC`, [uuid]);
|
|
23
26
|
const headings = headingRows.map((h) => mapHeading(h, refs));
|
|
27
|
+
// OWN-DEADLINE UNIFORM: `--overdue` narrows the child TO-DOS to those whose
|
|
28
|
+
// OWN deadline is overdue (open, strictly before today) via the shared
|
|
29
|
+
// OVERDUE predicate — NO recursion into anything, and the project header
|
|
30
|
+
// itself always renders. Headings that keep no surviving child collapse
|
|
31
|
+
// (below). Its single packed-today bind trails the two uuid binds.
|
|
32
|
+
const overdueSql = overdue ? ` AND ${OVERDUE}` : "";
|
|
33
|
+
const overdueBinds = overdue ? [encodePackedDate(localToday(now))] : [];
|
|
34
|
+
// Tag scope (§9a): the child to-dos are filtered by a tag carried DIRECTLY on
|
|
35
|
+
// the row — the container semantics. The project's own tags are inherited by
|
|
36
|
+
// EVERY child, so an inheritance-inclusive `--tag` would be vacuous here;
|
|
37
|
+
// suppressing the container hop makes `--tag` mean "children with this tag on
|
|
38
|
+
// themselves" (still descendant-expanded), and `--untagged` "children with no
|
|
39
|
+
// direct tag". No recursion; the header always renders. The tag binds trail
|
|
40
|
+
// the two uuid binds and lead the overdue bind, matching their left-to-right
|
|
41
|
+
// order in the SQL.
|
|
42
|
+
const tf = tagFilter(db, filter, { container: true });
|
|
24
43
|
const childRows = fetchTaskRows(db, `t.type = 0 AND (t.project = ? OR t.heading IN (
|
|
25
44
|
SELECT uuid FROM TMTask WHERE type = 2 AND project = ?
|
|
26
|
-
))
|
|
27
|
-
ORDER BY t."index" ASC`, [uuid, uuid]);
|
|
45
|
+
))${tf.sql}${overdueSql}
|
|
46
|
+
ORDER BY t."index" ASC`, [uuid, uuid, ...tf.binds, ...overdueBinds]);
|
|
28
47
|
const childTags = tagsOf(childRows);
|
|
29
48
|
const boundary = logBoundary(db, now);
|
|
30
49
|
const todos = childRows.map((r) => ({
|
|
@@ -40,6 +59,10 @@ export function projectView(db, uuid, now) {
|
|
|
40
59
|
const someday = [];
|
|
41
60
|
const logged = [];
|
|
42
61
|
const trashed = [];
|
|
62
|
+
// Every untrashed, non-template OPEN child, regardless of which bucket it
|
|
63
|
+
// lands in below — surfaced (only when the project is itself resolved) as
|
|
64
|
+
// the PLOG1 stranded-open-child count.
|
|
65
|
+
let openChildren = 0;
|
|
43
66
|
for (const { row, todo } of todos) {
|
|
44
67
|
if (row.trashed === 1) {
|
|
45
68
|
trashed.push(todo);
|
|
@@ -49,6 +72,8 @@ export function projectView(db, uuid, now) {
|
|
|
49
72
|
repeating.push(todo);
|
|
50
73
|
continue;
|
|
51
74
|
}
|
|
75
|
+
if (row.status === 0)
|
|
76
|
+
openChildren += 1;
|
|
52
77
|
if (row.status !== 0) {
|
|
53
78
|
// Completion ≠ logged: closed items the log-move sweep has not
|
|
54
79
|
// passed stay checked IN PLACE (their heading / the active block),
|
|
@@ -90,23 +115,29 @@ export function projectView(db, uuid, now) {
|
|
|
90
115
|
logged.sort((a, b) => (b.stopped?.getTime() ?? 0) - (a.stopped?.getTime() ?? 0));
|
|
91
116
|
const scheduled = [];
|
|
92
117
|
// Within a day the UI sorts by todayIndex ASC (Upcoming drag order).
|
|
93
|
-
for (const { date, todo } of scheduledRows.
|
|
118
|
+
for (const { date, todo } of scheduledRows.toSorted((a, b) => a.date.localeCompare(b.date) || a.ti - b.ti)) {
|
|
94
119
|
const last = scheduled[scheduled.length - 1];
|
|
95
120
|
if (last && last.date === date)
|
|
96
121
|
last.items.push(todo);
|
|
97
122
|
else
|
|
98
123
|
scheduled.push({ date, items: [todo] });
|
|
99
124
|
}
|
|
125
|
+
// Under any content scope (`--overdue` or a tag filter), a heading whose
|
|
126
|
+
// children were all filtered out collapses rather than rendering an empty
|
|
127
|
+
// section; a heading with a surviving child is kept. With no scope active
|
|
128
|
+
// every heading renders (its own empty state).
|
|
129
|
+
const contentScoped = overdue || tf.sql !== "";
|
|
130
|
+
const headingGroups = headings
|
|
131
|
+
.map((heading) => ({ heading, items: byHeading.get(heading.uuid) ?? [] }))
|
|
132
|
+
.filter((g) => !contentScoped || g.items.length > 0);
|
|
100
133
|
return {
|
|
101
134
|
project,
|
|
102
135
|
active,
|
|
103
|
-
headings:
|
|
104
|
-
heading,
|
|
105
|
-
items: byHeading.get(heading.uuid) ?? [],
|
|
106
|
-
})),
|
|
136
|
+
headings: headingGroups,
|
|
107
137
|
later: { scheduled, repeating, someday },
|
|
108
138
|
logged,
|
|
109
139
|
trashed,
|
|
140
|
+
openChildrenWhileResolved: project.status === "open" ? 0 : openChildren,
|
|
110
141
|
};
|
|
111
142
|
}
|
|
112
143
|
//# sourceMappingURL=project-view.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-view.js","sourceRoot":"","sources":["../../src/read/project-view.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEnF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAgB,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAClG,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"project-view.js","sourceRoot":"","sources":["../../src/read/project-view.ts"],"names":[],"mappings":"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"}
|
package/dist/read/queries.d.ts
CHANGED
|
@@ -9,13 +9,51 @@ import type { ChecklistRow, TaskRow } from "../model/mappers.ts";
|
|
|
9
9
|
export declare const NOT_TEMPLATE = "(t.rt1_recurrenceRule IS NULL AND t.repeater IS NULL)";
|
|
10
10
|
/**
|
|
11
11
|
* UI-faithful tag membership for list filtering: direct tag, or inherited
|
|
12
|
-
* through the ancestor chain heading → project → area
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* through the ancestor chain heading → project → area — the OR of every
|
|
13
|
+
* {@link INHERITANCE_CLAUSES} hop. Takes a SET of tag uuids (the target plus its
|
|
14
|
+
* hierarchy descendants); each hop gets the full set, so callers bind
|
|
15
|
+
* `uuids.length * INHERITANCE_CLAUSES.length` values via {@link tagScopeBinds}.
|
|
16
16
|
*/
|
|
17
17
|
export declare function tagScopeSql(uuidCount: number): string;
|
|
18
|
+
/**
|
|
19
|
+
* The bind list for {@link tagScopeSql}: the uuid set repeated once per hop, in
|
|
20
|
+
* clause order. Derived from the clause count so the bind multiplicity can never
|
|
21
|
+
* drift from the number of hops the SQL actually emits.
|
|
22
|
+
*/
|
|
18
23
|
export declare function tagScopeBinds(uuids: string[]): string[];
|
|
24
|
+
/**
|
|
25
|
+
* The DIRECT-ONLY projection of {@link tagScopeSql}: {@link DIRECT_TAG_CLAUSE}
|
|
26
|
+
* alone — the item's own `TMTaskTag` assignments — WITHOUT the five container-
|
|
27
|
+
* inheritance hops (project/area/heading). This is the SQL behind the CONTAINER
|
|
28
|
+
* `--tag` (the `project show` / `area show` / `projects` list views): every
|
|
29
|
+
* child inherits its container's tags, so the inheritance-inclusive relation is
|
|
30
|
+
* vacuous there — matching a DIRECT assignment is the useful, GUI-faithful
|
|
31
|
+
* behavior. It keeps tag-hierarchy descendant expansion (the uuid SET is still
|
|
32
|
+
* the tag plus its descendants, OR-matched) but drops container inheritance, so
|
|
33
|
+
* an item matches only when it is DIRECTLY tagged. Takes the uuid set once, so
|
|
34
|
+
* callers bind `uuids` exactly one time (not `× 6`).
|
|
35
|
+
*/
|
|
36
|
+
export declare function directTagScopeSql(uuidCount: number): string;
|
|
37
|
+
/**
|
|
38
|
+
* The negation of tag membership — the SQL behind the `untagged` filter (the
|
|
39
|
+
* GUI's "No Tag"). It negates the SAME {@link INHERITANCE_CLAUSES} relation with
|
|
40
|
+
* the tag-set restriction dropped: "carries ANY tag by any hop", wrapped in NOT.
|
|
41
|
+
* An item is untagged iff NO possible `--tag X` could ever match it — so this
|
|
42
|
+
* negates the whole membership relation, not merely the row's own direct
|
|
43
|
+
* assignments. Takes no binds.
|
|
44
|
+
*/
|
|
45
|
+
export declare function untaggedScopeSql(): string;
|
|
46
|
+
/**
|
|
47
|
+
* The DIRECT-ONLY counterpart of {@link untaggedScopeSql} — the SQL behind the
|
|
48
|
+
* CONTAINER `--untagged` (the GUI's in-context "No Tag" inside a project/area
|
|
49
|
+
* card). It negates only {@link DIRECT_TAG_CLAUSE} (the item's OWN direct
|
|
50
|
+
* assignments), leaving container inheritance untouched: an item qualifies when
|
|
51
|
+
* it carries no DIRECT tag, even if it inherits one from its project/area/
|
|
52
|
+
* heading. Every child inherits the container's tags, so the whole-relation
|
|
53
|
+
* {@link untaggedScopeSql} would exclude every row there — direct-only is the
|
|
54
|
+
* useful negation. Takes no binds.
|
|
55
|
+
*/
|
|
56
|
+
export declare function directUntaggedScopeSql(): string;
|
|
19
57
|
/**
|
|
20
58
|
* A tag plus every hierarchy descendant. Filtering by a parent tag matches
|
|
21
59
|
* child-tagged items — DOCUMENTED app behavior (the UI's tag filter works
|
|
@@ -39,7 +77,43 @@ export declare function tagWithDescendants(db: DatabaseSync, uuid: string): stri
|
|
|
39
77
|
* input passes through untouched (after trimming).
|
|
40
78
|
*/
|
|
41
79
|
export declare function stripThingsUri(ref: string): string;
|
|
42
|
-
|
|
80
|
+
/**
|
|
81
|
+
* The single source for uuid-miss not-found copy. Reused by the read-side
|
|
82
|
+
* resolvers, the write guards, and the MCP item lookups so a uuid/partial-uuid
|
|
83
|
+
* that matches nothing reads identically wherever the miss is reported.
|
|
84
|
+
*/
|
|
85
|
+
export declare function noUuidMatch(entity: string, ref: string): string;
|
|
86
|
+
/** A disambiguation candidate for a reference-resolution failure. */
|
|
87
|
+
export interface RefCandidate {
|
|
88
|
+
uuid: string;
|
|
89
|
+
title: string;
|
|
90
|
+
/** Optional context that distinguishes same-named candidates (area for a project, parent path for a tag). */
|
|
91
|
+
context?: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* A reference (uuid / partial-uuid / name) that did not resolve to exactly one
|
|
95
|
+
* entity. Extends RangeError so every existing `instanceof RangeError` handler
|
|
96
|
+
* keeps treating it as a usage-class failure — but the surfaces that know about
|
|
97
|
+
* it (CLI --json envelope, MCP tool result) additionally lift the structured
|
|
98
|
+
* `candidates` onto `error.details.candidates` so an agent can self-correct
|
|
99
|
+
* without re-parsing the prose message. `code` mirrors the envelope error code.
|
|
100
|
+
*
|
|
101
|
+
* PUBLIC API — exported from src/index.ts. This is the one error the consumer
|
|
102
|
+
* surfaces catch to render structured disambiguation; its `code`
|
|
103
|
+
* ("not-found" | "ambiguous") and `candidates` ({@link RefCandidate}[]) are the
|
|
104
|
+
* documented machine shape (docs/design/architecture.md, Consumer boundary).
|
|
105
|
+
*/
|
|
106
|
+
export declare class ReferenceResolutionError extends RangeError {
|
|
107
|
+
readonly code: "not-found" | "ambiguous";
|
|
108
|
+
readonly ref: string;
|
|
109
|
+
readonly candidates: RefCandidate[];
|
|
110
|
+
constructor(message: string, opts: {
|
|
111
|
+
code: "not-found" | "ambiguous";
|
|
112
|
+
ref: string;
|
|
113
|
+
candidates?: RefCandidate[];
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
export declare function resolveTaskUuidPrefix(db: DatabaseSync, refRaw: string, entity?: string): string;
|
|
43
117
|
/**
|
|
44
118
|
* Fold a name to its match key: NFC + case-fold + strip all whitespace and
|
|
45
119
|
* dashes/hyphens (ASCII hyphen, the U+2010–2015 dash block, U+2212 minus).
|
|
@@ -54,6 +128,15 @@ export interface NamedResolution {
|
|
|
54
128
|
} | null;
|
|
55
129
|
/** 0 = not found, 1 = ok, >1 = ambiguous at the deciding tier. */
|
|
56
130
|
matches: number;
|
|
131
|
+
/**
|
|
132
|
+
* The rows at the deciding tier when it was ambiguous (matches > 1) — the
|
|
133
|
+
* candidates a fail-closed resolver lists so the caller can disambiguate by
|
|
134
|
+
* uuid. Absent when resolved or not-found.
|
|
135
|
+
*/
|
|
136
|
+
candidates?: {
|
|
137
|
+
uuid: string;
|
|
138
|
+
title: string;
|
|
139
|
+
}[];
|
|
57
140
|
}
|
|
58
141
|
/**
|
|
59
142
|
* Tiered reference resolution (docs/design/reference-resolution.md): exact
|
|
@@ -62,7 +145,25 @@ export interface NamedResolution {
|
|
|
62
145
|
* ambiguous; no tier is not-found. Shared by the read-side `resolve*Uuid`
|
|
63
146
|
* throwers and the write-side `resolve*` (ContainerResolution) helpers.
|
|
64
147
|
*/
|
|
65
|
-
export declare function resolveNamedRef(db: DatabaseSync, table: string, extraWhere: string, extraBinds: (string | number)[], refRaw: string
|
|
148
|
+
export declare function resolveNamedRef(db: DatabaseSync, table: string, extraWhere: string, extraBinds: (string | number)[], refRaw: string, options?: {
|
|
149
|
+
prefixTier?: boolean;
|
|
150
|
+
}): NamedResolution;
|
|
151
|
+
/**
|
|
152
|
+
* Resolve a PROJECT write target from a uuid, partial-uuid, or unique name.
|
|
153
|
+
* Project write verbs (`things project update <ref>`, etc.) accept names
|
|
154
|
+
* through this; to-do and heading write targets stay uuid-only.
|
|
155
|
+
*
|
|
156
|
+
* A uuid / unique uuid-prefix resolves FIRST over every task (reusing
|
|
157
|
+
* {@link resolveTaskUuidPrefix}), so a wrong-TYPE id — a to-do uuid handed to a
|
|
158
|
+
* project verb — passes through to the op's own guard, which reports it with a
|
|
159
|
+
* targeted "that is a to-do, not a project" message rather than a misleading
|
|
160
|
+
* not-found. Otherwise the ref resolves as a project NAME through the SAME
|
|
161
|
+
* tiered {@link resolveNamedRef} matching the read side uses (shared core, not
|
|
162
|
+
* a fork) across projects (trashed included, for `project restore`), fail-
|
|
163
|
+
* closed with a candidate listing on an ambiguous name so a duplicated project
|
|
164
|
+
* title is disambiguated by uuid rather than guessed.
|
|
165
|
+
*/
|
|
166
|
+
export declare function resolveProjectWriteTarget(db: DatabaseSync, refRaw: string): string;
|
|
66
167
|
export declare function resolveTagUuid(db: DatabaseSync, ref: string): string;
|
|
67
168
|
/**
|
|
68
169
|
* Write destinations stay strict (a trashed project is not a valid target);
|
|
@@ -71,12 +172,59 @@ export declare function resolveTagUuid(db: DatabaseSync, ref: string): string;
|
|
|
71
172
|
*/
|
|
72
173
|
export declare function resolveProjectUuid(db: DatabaseSync, ref: string, options?: {
|
|
73
174
|
trashed?: boolean;
|
|
175
|
+
prefixTier?: boolean;
|
|
74
176
|
}): string;
|
|
75
|
-
export declare function resolveAreaUuid(db: DatabaseSync, ref: string
|
|
177
|
+
export declare function resolveAreaUuid(db: DatabaseSync, ref: string, options?: {
|
|
178
|
+
prefixTier?: boolean;
|
|
179
|
+
}): string;
|
|
180
|
+
/**
|
|
181
|
+
* A row's EFFECTIVE area: its own `area` link, else the area of its project,
|
|
182
|
+
* else the area of its heading's project. To-dos nested in a project (or under a
|
|
183
|
+
* heading) carry `area = NULL` in the DB — the area lives on the container — so
|
|
184
|
+
* this resolves the nearest area walking the SAME chain the tag-inheritance SQL
|
|
185
|
+
* uses (t.area → t.project's area → t.heading's project's area). Projects carry
|
|
186
|
+
* their area directly (project/heading are NULL), so COALESCE returns `t.area`
|
|
187
|
+
* unchanged for them — areas are not inherited. Surfaced as the entity's `area`
|
|
188
|
+
* Ref (mappers.ts); whether it is direct vs effective stays derivable from
|
|
189
|
+
* whether `project`/`heading` is set. Emitted as the extra `effectiveArea`
|
|
190
|
+
* column so the raw `t.area` (which tag inheritance and the write layer read)
|
|
191
|
+
* stays available.
|
|
192
|
+
*/
|
|
193
|
+
export declare const EFFECTIVE_AREA = "COALESCE(\n t.area,\n (SELECT p.area FROM TMTask p WHERE p.uuid = t.project),\n (SELECT hp.area FROM TMTask h JOIN TMTask hp ON hp.uuid = h.project WHERE h.uuid = t.heading)\n)";
|
|
76
194
|
export declare function fetchTaskRows(db: DatabaseSync, where: string, params?: unknown[]): TaskRow[];
|
|
77
195
|
export declare function fetchTaskByUuid(db: DatabaseSync, uuid: string): TaskRow | null;
|
|
78
196
|
export declare function fetchChecklistRows(db: DatabaseSync, taskUuid: string): ChecklistRow[];
|
|
79
|
-
/**
|
|
197
|
+
/**
|
|
198
|
+
* Direct tags for a set of tasks, in one query. Returns uuid -> Ref[] in the
|
|
199
|
+
* app's CANONICAL tag order.
|
|
200
|
+
*
|
|
201
|
+
* CANONICAL ORDER (ratified 2026-07-14; tiebreak corrected 2026-07-15 by
|
|
202
|
+
* TAGORD1): `TMTag."index"` (INTEGER, often negative) is the user-draggable
|
|
203
|
+
* order from the app's Tags window, and the GUI renders every multi-tag pill row
|
|
204
|
+
* in ascending `index`. Live oracle: the `Replace CPAP mask & air filter` to-do
|
|
205
|
+
* shows `#recurring #home #housekeeping`, matching the tags' indexes, NOT their
|
|
206
|
+
* alphabetical order.
|
|
207
|
+
*
|
|
208
|
+
* TIEBREAK = `uuid`, NOT `title` (TAGORD1 lab oracle, docs/lab/taglab-probes.md).
|
|
209
|
+
* Never-dragged tags ubiquitously tie at `index = 0`; the app breaks that tie by
|
|
210
|
+
* the tag's UUID (ascending ASCII), NOT alphabetically. Proven across three
|
|
211
|
+
* surfaces in a VM (Tags window, a to-do's multi-tag pill row — input-order
|
|
212
|
+
* independent, and the list filter-bar chips): 8 tags seeded reverse-alpha all
|
|
213
|
+
* tied at 0 displayed in exact uuid order, and `ORDER BY "index", uuid`
|
|
214
|
+
* reproduced the whole Tags-window order byte-for-byte where `ORDER BY "index",
|
|
215
|
+
* title` diverged. TMTag has no creation-date column, so creation order is not
|
|
216
|
+
* even a candidate comparator.
|
|
217
|
+
*
|
|
218
|
+
* NESTED-TAG CAVEAT (open question, deliberately unsolved): child tags' indexes
|
|
219
|
+
* interleave globally with top-level ones — CONFIRMED by TAGORD1: `TMTag."index"`
|
|
220
|
+
* is a single GLOBAL space, not per-parent (a seeded child landed at -378 among
|
|
221
|
+
* root tags at 0/-35/-67). So a flat-index sort can place a child BEFORE its
|
|
222
|
+
* parent in a multi-tag row. No live item carries a nested tag alongside another
|
|
223
|
+
* tag, so there is no GUI oracle for the interleaved case — flat ascending
|
|
224
|
+
* `index` is the ratified comparator, isolated HERE. If a GUI oracle ever
|
|
225
|
+
* contradicts it, the fix is a DFS-rank swap in this one ORDER BY (rank children
|
|
226
|
+
* after parents).
|
|
227
|
+
*/
|
|
80
228
|
export declare function fetchTagsForTasks(db: DatabaseSync, taskUuids: string[]): Map<string, Ref[]>;
|
|
81
229
|
/**
|
|
82
230
|
* Lazy heading-uuid -> owning-project Ref resolver, cached per instance.
|