things-api 0.5.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.
Files changed (184) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/bin/things.js +9 -0
  4. package/dist/audit/log.d.ts +10 -0
  5. package/dist/audit/log.js +31 -0
  6. package/dist/audit/log.js.map +1 -0
  7. package/dist/audit/schema.d.ts +37 -0
  8. package/dist/audit/schema.js +9 -0
  9. package/dist/audit/schema.js.map +1 -0
  10. package/dist/cli/commands/doctor.d.ts +9 -0
  11. package/dist/cli/commands/doctor.js +60 -0
  12. package/dist/cli/commands/doctor.js.map +1 -0
  13. package/dist/cli/commands/mcp.d.ts +7 -0
  14. package/dist/cli/commands/mcp.js +26 -0
  15. package/dist/cli/commands/mcp.js.map +1 -0
  16. package/dist/cli/commands/project.d.ts +5 -0
  17. package/dist/cli/commands/project.js +50 -0
  18. package/dist/cli/commands/project.js.map +1 -0
  19. package/dist/cli/commands/reads.d.ts +21 -0
  20. package/dist/cli/commands/reads.js +295 -0
  21. package/dist/cli/commands/reads.js.map +1 -0
  22. package/dist/cli/commands/snapshot.d.ts +7 -0
  23. package/dist/cli/commands/snapshot.js +22 -0
  24. package/dist/cli/commands/snapshot.js.map +1 -0
  25. package/dist/cli/commands/todo.d.ts +6 -0
  26. package/dist/cli/commands/todo.js +43 -0
  27. package/dist/cli/commands/todo.js.map +1 -0
  28. package/dist/cli/commands/writes.d.ts +9 -0
  29. package/dist/cli/commands/writes.js +994 -0
  30. package/dist/cli/commands/writes.js.map +1 -0
  31. package/dist/cli/exit-codes.d.ts +26 -0
  32. package/dist/cli/exit-codes.js +26 -0
  33. package/dist/cli/exit-codes.js.map +1 -0
  34. package/dist/cli/main.d.ts +4 -0
  35. package/dist/cli/main.js +73 -0
  36. package/dist/cli/main.js.map +1 -0
  37. package/dist/cli/output.d.ts +42 -0
  38. package/dist/cli/output.js +16 -0
  39. package/dist/cli/output.js.map +1 -0
  40. package/dist/cli/style.d.ts +6 -0
  41. package/dist/cli/style.js +25 -0
  42. package/dist/cli/style.js.map +1 -0
  43. package/dist/client.d.ts +184 -0
  44. package/dist/client.js +187 -0
  45. package/dist/client.js.map +1 -0
  46. package/dist/config.d.ts +31 -0
  47. package/dist/config.js +76 -0
  48. package/dist/config.js.map +1 -0
  49. package/dist/contracts.d.ts +79 -0
  50. package/dist/contracts.js +46 -0
  51. package/dist/contracts.js.map +1 -0
  52. package/dist/db/baselines/db-v26.d.ts +8 -0
  53. package/dist/db/baselines/db-v26.js +16 -0
  54. package/dist/db/baselines/db-v26.js.map +1 -0
  55. package/dist/db/baselines/index.d.ts +3 -0
  56. package/dist/db/baselines/index.js +4 -0
  57. package/dist/db/baselines/index.js.map +1 -0
  58. package/dist/db/connection.d.ts +22 -0
  59. package/dist/db/connection.js +40 -0
  60. package/dist/db/connection.js.map +1 -0
  61. package/dist/db/fingerprint.d.ts +42 -0
  62. package/dist/db/fingerprint.js +82 -0
  63. package/dist/db/fingerprint.js.map +1 -0
  64. package/dist/db/locate.d.ts +13 -0
  65. package/dist/db/locate.js +44 -0
  66. package/dist/db/locate.js.map +1 -0
  67. package/dist/db/schema.d.ts +37 -0
  68. package/dist/db/schema.js +81 -0
  69. package/dist/db/schema.js.map +1 -0
  70. package/dist/diagnose.d.ts +67 -0
  71. package/dist/diagnose.js +143 -0
  72. package/dist/diagnose.js.map +1 -0
  73. package/dist/index.d.ts +46 -0
  74. package/dist/index.js +23 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/mcp/server.d.ts +8 -0
  77. package/dist/mcp/server.js +1002 -0
  78. package/dist/mcp/server.js.map +1 -0
  79. package/dist/model/dates.d.ts +38 -0
  80. package/dist/model/dates.js +104 -0
  81. package/dist/model/dates.js.map +1 -0
  82. package/dist/model/entities.d.ts +117 -0
  83. package/dist/model/entities.js +21 -0
  84. package/dist/model/entities.js.map +1 -0
  85. package/dist/model/mappers.d.ts +55 -0
  86. package/dist/model/mappers.js +105 -0
  87. package/dist/model/mappers.js.map +1 -0
  88. package/dist/model/occurrences.d.ts +46 -0
  89. package/dist/model/occurrences.js +164 -0
  90. package/dist/model/occurrences.js.map +1 -0
  91. package/dist/model/recurrence.d.ts +52 -0
  92. package/dist/model/recurrence.js +164 -0
  93. package/dist/model/recurrence.js.map +1 -0
  94. package/dist/paths.d.ts +6 -0
  95. package/dist/paths.js +30 -0
  96. package/dist/paths.js.map +1 -0
  97. package/dist/read/detail.d.ts +8 -0
  98. package/dist/read/detail.js +34 -0
  99. package/dist/read/detail.js.map +1 -0
  100. package/dist/read/project-view.d.ts +34 -0
  101. package/dist/read/project-view.js +87 -0
  102. package/dist/read/project-view.js.map +1 -0
  103. package/dist/read/queries.d.ts +39 -0
  104. package/dist/read/queries.js +134 -0
  105. package/dist/read/queries.js.map +1 -0
  106. package/dist/read/snapshot.d.ts +25 -0
  107. package/dist/read/snapshot.js +39 -0
  108. package/dist/read/snapshot.js.map +1 -0
  109. package/dist/read/tags.d.ts +17 -0
  110. package/dist/read/tags.js +64 -0
  111. package/dist/read/tags.js.map +1 -0
  112. package/dist/read/views.d.ts +152 -0
  113. package/dist/read/views.js +322 -0
  114. package/dist/read/views.js.map +1 -0
  115. package/dist/surface-copy.d.ts +15 -0
  116. package/dist/surface-copy.js +16 -0
  117. package/dist/surface-copy.js.map +1 -0
  118. package/dist/write/automation-probe.d.ts +12 -0
  119. package/dist/write/automation-probe.js +59 -0
  120. package/dist/write/automation-probe.js.map +1 -0
  121. package/dist/write/batch.d.ts +51 -0
  122. package/dist/write/batch.js +97 -0
  123. package/dist/write/batch.js.map +1 -0
  124. package/dist/write/capabilities.d.ts +16 -0
  125. package/dist/write/capabilities.js +16 -0
  126. package/dist/write/capabilities.js.map +1 -0
  127. package/dist/write/commands.d.ts +32 -0
  128. package/dist/write/commands.js +1274 -0
  129. package/dist/write/commands.js.map +1 -0
  130. package/dist/write/environment.d.ts +29 -0
  131. package/dist/write/environment.js +105 -0
  132. package/dist/write/environment.js.map +1 -0
  133. package/dist/write/experimental.d.ts +7 -0
  134. package/dist/write/experimental.js +39 -0
  135. package/dist/write/experimental.js.map +1 -0
  136. package/dist/write/failure-hints.d.ts +32 -0
  137. package/dist/write/failure-hints.js +71 -0
  138. package/dist/write/failure-hints.js.map +1 -0
  139. package/dist/write/guards.d.ts +21 -0
  140. package/dist/write/guards.js +312 -0
  141. package/dist/write/guards.js.map +1 -0
  142. package/dist/write/lock.d.ts +10 -0
  143. package/dist/write/lock.js +70 -0
  144. package/dist/write/lock.js.map +1 -0
  145. package/dist/write/operations.d.ts +262 -0
  146. package/dist/write/operations.js +34 -0
  147. package/dist/write/operations.js.map +1 -0
  148. package/dist/write/pipeline.d.ts +90 -0
  149. package/dist/write/pipeline.js +375 -0
  150. package/dist/write/pipeline.js.map +1 -0
  151. package/dist/write/planner.d.ts +40 -0
  152. package/dist/write/planner.js +57 -0
  153. package/dist/write/planner.js.map +1 -0
  154. package/dist/write/pre-state.d.ts +125 -0
  155. package/dist/write/pre-state.js +299 -0
  156. package/dist/write/pre-state.js.map +1 -0
  157. package/dist/write/reopen.d.ts +29 -0
  158. package/dist/write/reopen.js +63 -0
  159. package/dist/write/reopen.js.map +1 -0
  160. package/dist/write/reorder.d.ts +19 -0
  161. package/dist/write/reorder.js +285 -0
  162. package/dist/write/reorder.js.map +1 -0
  163. package/dist/write/undo.d.ts +53 -0
  164. package/dist/write/undo.js +703 -0
  165. package/dist/write/undo.js.map +1 -0
  166. package/dist/write/vectors/applescript.d.ts +5 -0
  167. package/dist/write/vectors/applescript.js +216 -0
  168. package/dist/write/vectors/applescript.js.map +1 -0
  169. package/dist/write/vectors/registry.d.ts +2 -0
  170. package/dist/write/vectors/registry.js +11 -0
  171. package/dist/write/vectors/registry.js.map +1 -0
  172. package/dist/write/vectors/types.d.ts +48 -0
  173. package/dist/write/vectors/types.js +2 -0
  174. package/dist/write/vectors/types.js.map +1 -0
  175. package/dist/write/vectors/url-scheme.d.ts +3 -0
  176. package/dist/write/vectors/url-scheme.js +176 -0
  177. package/dist/write/vectors/url-scheme.js.map +1 -0
  178. package/dist/write/verify/delta.d.ts +126 -0
  179. package/dist/write/verify/delta.js +281 -0
  180. package/dist/write/verify/delta.js.map +1 -0
  181. package/dist/write/verify/poller.d.ts +24 -0
  182. package/dist/write/verify/poller.js +28 -0
  183. package/dist/write/verify/poller.js.map +1 -0
  184. package/package.json +52 -0
@@ -0,0 +1,164 @@
1
+ /** Hard cap on calendar cells examined — a runaway-rule backstop. */
2
+ const MAX_ITERATIONS = 1000;
3
+ // ------------------------------------------------------------ date helpers
4
+ // UTC-midnight arithmetic: IsoDate ↔ epoch days, no timezone hazards.
5
+ function toUtc(iso) {
6
+ return new Date(`${iso}T00:00:00Z`);
7
+ }
8
+ function toIso(d) {
9
+ return d.toISOString().slice(0, 10);
10
+ }
11
+ function addDays(iso, days) {
12
+ const d = toUtc(iso);
13
+ d.setUTCDate(d.getUTCDate() + days);
14
+ return toIso(d);
15
+ }
16
+ function weekdayOf(iso) {
17
+ return toUtc(iso).getUTCDay(); // 0 = Sunday, matching the rule encoding
18
+ }
19
+ function lastDayOfMonth(year, month1) {
20
+ return new Date(Date.UTC(year, month1, 0)).getUTCDate();
21
+ }
22
+ function isoFor(year, month1, day) {
23
+ return `${String(year).padStart(4, "0")}-${String(month1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
24
+ }
25
+ /** Resolve a monthly/yearly day offset within a month; null = no such date. */
26
+ function dayInMonth(year, month1, offset) {
27
+ const last = lastDayOfMonth(year, month1);
28
+ if (offset.weekday !== undefined) {
29
+ // nth weekday (weekdayOrdinal 1..5, -1 = last)
30
+ const ordinal = offset.weekdayOrdinal ?? 1;
31
+ if (ordinal === -1) {
32
+ for (let day = last; day >= last - 6; day--) {
33
+ const iso = isoFor(year, month1, day);
34
+ if (weekdayOf(iso) === offset.weekday)
35
+ return iso;
36
+ }
37
+ return null;
38
+ }
39
+ const firstWeekday = weekdayOf(isoFor(year, month1, 1));
40
+ const day = 1 + ((offset.weekday - firstWeekday + 7) % 7) + (ordinal - 1) * 7;
41
+ return day <= last ? isoFor(year, month1, day) : null; // missing 5th → skip
42
+ }
43
+ const day = offset.day ?? 1;
44
+ if (day === -1)
45
+ return isoFor(year, month1, last);
46
+ return isoFor(year, month1, Math.min(day, last)); // clamp (see header)
47
+ }
48
+ // ------------------------------------------------------------- generation
49
+ /**
50
+ * Event dates of a FIXED rule from `anchor` (an app-materialized event date,
51
+ * INCLUDED as the first result), honoring the rule's endDate/remainingCount
52
+ * bounds and the window's count/until. Throws on after-completion rules.
53
+ */
54
+ export function generateEventDates(rule, anchor, window) {
55
+ if (rule.type !== "fixed") {
56
+ throw new RangeError("after-completion rules cannot be projected — the next date depends on when the " +
57
+ "prior instance resolves");
58
+ }
59
+ // remainingCount counts occurrences the rule will still produce; the
60
+ // anchor is the first of them.
61
+ const cap = Math.min(Math.max(1, window.count), rule.remainingCount === null ? Number.POSITIVE_INFINITY : rule.remainingCount);
62
+ const inBounds = (iso) => (window.until === undefined || iso <= window.until) &&
63
+ (rule.endDate === null || iso <= rule.endDate);
64
+ // Every unit generates in ascending date order, so the first out-of-bounds
65
+ // date terminates the walk. Returns "keep generating".
66
+ const out = [];
67
+ const push = (iso) => {
68
+ if (iso < anchor)
69
+ return true; // pre-anchor cell (same week/month) — skip
70
+ if (!inBounds(iso))
71
+ return false; // past until/endDate — done
72
+ if (out.at(-1) !== iso)
73
+ out.push(iso);
74
+ return out.length < cap;
75
+ };
76
+ switch (rule.unit) {
77
+ case "daily": {
78
+ let date = anchor;
79
+ for (let i = 0; i < MAX_ITERATIONS; i++) {
80
+ if (!push(date))
81
+ break;
82
+ date = addDays(date, rule.interval);
83
+ }
84
+ break;
85
+ }
86
+ case "weekly": {
87
+ const weekdays = [
88
+ ...new Set(rule.offsets
89
+ .map((o) => o.weekday)
90
+ .filter((w) => w !== undefined)
91
+ .toSorted((a, b) => a - b)),
92
+ ];
93
+ const days = weekdays.length > 0 ? weekdays : [weekdayOf(anchor)];
94
+ const cohortStart = addDays(anchor, -weekdayOf(anchor)); // Sunday of the anchor's week
95
+ outer: for (let week = 0; week < MAX_ITERATIONS; week++) {
96
+ const weekStart = addDays(cohortStart, week * rule.interval * 7);
97
+ for (const wd of days) {
98
+ if (!push(addDays(weekStart, wd)))
99
+ break outer;
100
+ }
101
+ }
102
+ break;
103
+ }
104
+ case "monthly": {
105
+ const offsets = rule.offsets.length > 0
106
+ ? rule.offsets
107
+ : [{ day: Number(anchor.slice(8, 10)) }];
108
+ const anchorYear = Number(anchor.slice(0, 4));
109
+ const anchorMonth = Number(anchor.slice(5, 7));
110
+ outer: for (let step = 0; step < MAX_ITERATIONS; step++) {
111
+ const monthIndex = anchorMonth - 1 + step * rule.interval;
112
+ const year = anchorYear + Math.floor(monthIndex / 12);
113
+ const month1 = (monthIndex % 12) + 1;
114
+ const dates = offsets
115
+ .map((o) => dayInMonth(year, month1, o))
116
+ .filter((d) => d !== null)
117
+ .toSorted();
118
+ for (const date of dates) {
119
+ if (!push(date))
120
+ break outer;
121
+ }
122
+ }
123
+ break;
124
+ }
125
+ case "yearly": {
126
+ const offsets = rule.offsets.length > 0
127
+ ? rule.offsets
128
+ : [
129
+ {
130
+ month: Number(anchor.slice(5, 7)),
131
+ day: Number(anchor.slice(8, 10)),
132
+ },
133
+ ];
134
+ const anchorYear = Number(anchor.slice(0, 4));
135
+ outer: for (let step = 0; step < MAX_ITERATIONS; step++) {
136
+ const year = anchorYear + step * rule.interval;
137
+ const dates = offsets
138
+ .map((o) => dayInMonth(year, o.month ?? Number(anchor.slice(5, 7)), o))
139
+ .filter((d) => d !== null)
140
+ .toSorted();
141
+ for (const date of dates) {
142
+ if (!push(date))
143
+ break outer;
144
+ }
145
+ }
146
+ break;
147
+ }
148
+ }
149
+ return out;
150
+ }
151
+ /**
152
+ * Project a template's next occurrences in INSTANCE terms. The anchor is the
153
+ * app-materialized next instance START date (rt1_nextInstanceStartDate);
154
+ * the rule's ts offset splits each event into startDate/deadline exactly as
155
+ * the app does when spawning (deadline = start − ts, validated live).
156
+ */
157
+ export function projectOccurrences(rule, nextInstanceStartDate, window) {
158
+ const anchorEvent = addDays(nextInstanceStartDate, -rule.startOffsetDays);
159
+ return generateEventDates(rule, anchorEvent, window).map((event) => ({
160
+ startDate: addDays(event, rule.startOffsetDays),
161
+ deadline: rule.startOffsetDays < 0 ? event : null,
162
+ }));
163
+ }
164
+ //# sourceMappingURL=occurrences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"occurrences.js","sourceRoot":"","sources":["../../src/model/occurrences.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * READ-ONLY decoder for TMTask.rt1_recurrenceRule — an XML plist Things
3
+ * writes for repeating templates. NEVER serialized or written back (repeat
4
+ * rules are UI-only; see docs/gaps.md). Semantics reverse-engineered from a
5
+ * 91-rule live corpus and cross-validated against the app's own spawned
6
+ * instances (deadline = startDate − ts held on every sample, 2026-07-04):
7
+ *
8
+ * tp 0 fixed schedule · 1 after-completion
9
+ * fu 16 daily · 256 weekly · 8 monthly · 4 yearly
10
+ * fa interval multiplier ("every fa units")
11
+ * ts start offset in days relative to the event date (≤0 = start early;
12
+ * the event date becomes the spawned instance's DEADLINE when ts<0)
13
+ * of occurrence offsets: dy (0-based day; -1 = last day of month),
14
+ * mo (0-based month), wd (weekday, 0=Sunday), wdo (nth weekday, -1=last)
15
+ * ed end date (unix seconds; distant-future sentinel = no end)
16
+ * rc remaining repeat count (0 = unlimited)
17
+ * rrv rule schema version (4 observed)
18
+ * sr/ia anchor timestamps (not needed for reads)
19
+ */
20
+ import type { IsoDate } from "./dates.ts";
21
+ export interface RepeatOffset {
22
+ /** 1-based day of month; -1 = last day. */
23
+ day?: number;
24
+ /** 1-based month. */
25
+ month?: number;
26
+ /** Weekday, 0 = Sunday … 6 = Saturday. */
27
+ weekday?: number;
28
+ /** Nth weekday within the month (1..5), -1 = last. */
29
+ weekdayOrdinal?: number;
30
+ }
31
+ export interface RepeatRule {
32
+ type: "fixed" | "after-completion";
33
+ unit: "daily" | "weekly" | "monthly" | "yearly";
34
+ interval: number;
35
+ /** Days the instance's start precedes its event date (≤ 0). */
36
+ startOffsetDays: number;
37
+ offsets: RepeatOffset[];
38
+ endDate: IsoDate | null;
39
+ remainingCount: number | null;
40
+ version: number;
41
+ }
42
+ export declare function decodeRecurrenceRule(blob: unknown): RepeatRule;
43
+ type PlistValue = number | string | boolean | PlistValue[] | {
44
+ [key: string]: PlistValue;
45
+ };
46
+ /**
47
+ * Parses the machine-generated plist subset Things emits (dict/array/
48
+ * integer/real/string/true/false). Deliberately tiny — not a general XML
49
+ * parser; unknown node kinds fail loudly.
50
+ */
51
+ export declare function parsePlist(xml: string): PlistValue;
52
+ export {};
@@ -0,0 +1,164 @@
1
+ const UNITS = {
2
+ 16: "daily",
3
+ 256: "weekly",
4
+ 8: "monthly",
5
+ 4: "yearly",
6
+ };
7
+ /** Unix seconds this far out (year ≥ 3000) mean "repeats forever". */
8
+ const DISTANT_FUTURE_EPOCH = 32503680000;
9
+ export function decodeRecurrenceRule(blob) {
10
+ const xml = typeof blob === "string"
11
+ ? blob
12
+ : blob instanceof Uint8Array
13
+ ? new TextDecoder().decode(blob)
14
+ : null;
15
+ if (xml === null)
16
+ throw new RangeError("recurrence rule is not an XML blob");
17
+ const root = parsePlist(xml);
18
+ if (typeof root !== "object" || root === null || Array.isArray(root)) {
19
+ throw new RangeError("recurrence rule plist is not a dict");
20
+ }
21
+ const dict = root;
22
+ const tp = num(dict, "tp");
23
+ const fu = num(dict, "fu");
24
+ const unit = UNITS[fu];
25
+ if (unit === undefined)
26
+ throw new RangeError(`unknown recurrence unit fu=${fu}`);
27
+ if (tp !== 0 && tp !== 1)
28
+ throw new RangeError(`unknown recurrence type tp=${tp}`);
29
+ const offsets = [];
30
+ const of = dict["of"];
31
+ if (Array.isArray(of)) {
32
+ for (const entry of of) {
33
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
34
+ continue;
35
+ const e = entry;
36
+ const offset = {};
37
+ if (typeof e["dy"] === "number")
38
+ offset.day = e["dy"] === -1 ? -1 : e["dy"] + 1;
39
+ if (typeof e["mo"] === "number")
40
+ offset.month = e["mo"] + 1;
41
+ if (typeof e["wd"] === "number")
42
+ offset.weekday = e["wd"];
43
+ if (typeof e["wdo"] === "number")
44
+ offset.weekdayOrdinal = e["wdo"];
45
+ offsets.push(offset);
46
+ }
47
+ }
48
+ const ed = typeof dict["ed"] === "number" ? dict["ed"] : null;
49
+ const rc = typeof dict["rc"] === "number" ? dict["rc"] : 0;
50
+ return {
51
+ type: tp === 0 ? "fixed" : "after-completion",
52
+ unit,
53
+ interval: num(dict, "fa"),
54
+ startOffsetDays: typeof dict["ts"] === "number" ? dict["ts"] : 0,
55
+ offsets,
56
+ endDate: ed === null || ed >= DISTANT_FUTURE_EPOCH ? null : epochToIso(ed),
57
+ remainingCount: rc === 0 ? null : rc,
58
+ version: typeof dict["rrv"] === "number" ? dict["rrv"] : 0,
59
+ };
60
+ }
61
+ function num(dict, key) {
62
+ const v = dict[key];
63
+ if (typeof v !== "number")
64
+ throw new RangeError(`recurrence rule missing numeric ${key}`);
65
+ return v;
66
+ }
67
+ function epochToIso(seconds) {
68
+ const d = new Date(seconds * 1000);
69
+ return `${String(d.getUTCFullYear()).padStart(4, "0")}-${String(d.getUTCMonth() + 1).padStart(2, "0")}-${String(d.getUTCDate()).padStart(2, "0")}`;
70
+ }
71
+ /**
72
+ * Parses the machine-generated plist subset Things emits (dict/array/
73
+ * integer/real/string/true/false). Deliberately tiny — not a general XML
74
+ * parser; unknown node kinds fail loudly.
75
+ */
76
+ export function parsePlist(xml) {
77
+ const body = /<plist[^>]*>([\s\S]*)<\/plist>/.exec(xml)?.[1];
78
+ if (body === undefined)
79
+ throw new RangeError("not a plist document");
80
+ const parser = new Parser(body);
81
+ const value = parser.parseValue();
82
+ parser.skipWs();
83
+ return value;
84
+ }
85
+ class Parser {
86
+ pos = 0;
87
+ s;
88
+ constructor(s) {
89
+ this.s = s;
90
+ }
91
+ skipWs() {
92
+ while (this.pos < this.s.length && /\s/.test(this.s[this.pos]))
93
+ this.pos++;
94
+ }
95
+ openTag() {
96
+ this.skipWs();
97
+ const m = /^<([a-z]+)\s*(\/)?>/.exec(this.s.slice(this.pos));
98
+ if (!m)
99
+ throw new RangeError(`plist parse error at ${this.pos}`);
100
+ this.pos += m[0].length;
101
+ return m[2] === "/" ? `${m[1]}/` : m[1];
102
+ }
103
+ closeTag(name) {
104
+ this.skipWs();
105
+ const expect = `</${name}>`;
106
+ if (!this.s.startsWith(expect, this.pos)) {
107
+ throw new RangeError(`plist parse error: expected ${expect} at ${this.pos}`);
108
+ }
109
+ this.pos += expect.length;
110
+ }
111
+ text(until) {
112
+ const idx = this.s.indexOf(`</${until}>`, this.pos);
113
+ if (idx === -1)
114
+ throw new RangeError(`plist parse error: unterminated <${until}>`);
115
+ const raw = this.s.slice(this.pos, idx);
116
+ this.pos = idx + until.length + 3;
117
+ return raw
118
+ .replaceAll("&lt;", "<")
119
+ .replaceAll("&gt;", ">")
120
+ .replaceAll("&quot;", '"')
121
+ .replaceAll("&apos;", "'")
122
+ .replaceAll("&amp;", "&");
123
+ }
124
+ peekClose(name) {
125
+ this.skipWs();
126
+ return this.s.startsWith(`</${name}>`, this.pos);
127
+ }
128
+ parseValue() {
129
+ const tag = this.openTag();
130
+ switch (tag) {
131
+ case "integer":
132
+ case "real":
133
+ return Number(this.text(tag));
134
+ case "string":
135
+ return this.text(tag);
136
+ case "true/":
137
+ return true;
138
+ case "false/":
139
+ return false;
140
+ case "dict": {
141
+ const dict = {};
142
+ while (!this.peekClose("dict")) {
143
+ const keyTag = this.openTag();
144
+ if (keyTag !== "key")
145
+ throw new RangeError(`plist parse error: expected <key>`);
146
+ const key = this.text("key");
147
+ dict[key] = this.parseValue();
148
+ }
149
+ this.closeTag("dict");
150
+ return dict;
151
+ }
152
+ case "array": {
153
+ const arr = [];
154
+ while (!this.peekClose("array"))
155
+ arr.push(this.parseValue());
156
+ this.closeTag("array");
157
+ return arr;
158
+ }
159
+ default:
160
+ throw new RangeError(`plist parse error: unsupported node <${tag}>`);
161
+ }
162
+ }
163
+ }
164
+ //# sourceMappingURL=recurrence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recurrence.js","sourceRoot":"","sources":["../../src/model/recurrence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function stateDir(env?: NodeJS.ProcessEnv): string;
2
+ export declare function configDir(env?: NodeJS.ProcessEnv): string;
3
+ export declare function auditDir(env?: NodeJS.ProcessEnv): string;
4
+ export declare function mutationLockPath(env?: NodeJS.ProcessEnv): string;
5
+ /** Environment tuple recorded at the last verified mutation (consent-churn tripwire). */
6
+ export declare function environmentStatePath(env?: NodeJS.ProcessEnv): string;
package/dist/paths.js ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * XDG-style directory resolution for state (audit log, lockfile, local
3
+ * acceptances) and config. Overridable via THINGS_API_* env for tests and
4
+ * non-standard setups.
5
+ */
6
+ import { homedir } from "node:os";
7
+ import { join } from "node:path";
8
+ export function stateDir(env = process.env) {
9
+ if (env["THINGS_API_STATE_DIR"])
10
+ return env["THINGS_API_STATE_DIR"];
11
+ const xdg = env["XDG_STATE_HOME"];
12
+ return join(xdg && xdg !== "" ? xdg : join(homedir(), ".local", "state"), "things-api");
13
+ }
14
+ export function configDir(env = process.env) {
15
+ if (env["THINGS_API_CONFIG_DIR"])
16
+ return env["THINGS_API_CONFIG_DIR"];
17
+ const xdg = env["XDG_CONFIG_HOME"];
18
+ return join(xdg && xdg !== "" ? xdg : join(homedir(), ".config"), "things-api");
19
+ }
20
+ export function auditDir(env = process.env) {
21
+ return join(stateDir(env), "audit");
22
+ }
23
+ export function mutationLockPath(env = process.env) {
24
+ return join(stateDir(env), "mutation.lock");
25
+ }
26
+ /** Environment tuple recorded at the last verified mutation (consent-churn tripwire). */
27
+ export function environmentStatePath(env = process.env) {
28
+ return join(stateDir(env), "environment.json");
29
+ }
30
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,UAAU,QAAQ,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC3D,IAAI,GAAG,CAAC,sBAAsB,CAAC;QAAE,OAAO,GAAG,CAAC,sBAAsB,CAAC,CAAC;IACpE,MAAM,GAAG,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC5D,IAAI,GAAG,CAAC,uBAAuB,CAAC;QAAE,OAAO,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACtE,MAAM,GAAG,GAAG,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,YAAY,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC3D,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACnE,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,eAAe,CAAC,CAAC;AAC9C,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,oBAAoB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACvE,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,kBAAkB,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Single-record reads by UUID — includes repeating templates (which list
3
+ * views hide), checklist items, and inherited tags.
4
+ */
5
+ import type { DatabaseSync } from "node:sqlite";
6
+ import type { AnyTask, ChecklistItem } from "../model/entities.ts";
7
+ export declare function byUuid(db: DatabaseSync, uuid: string): AnyTask | null;
8
+ export declare function checklistFor(db: DatabaseSync, taskUuid: string): ChecklistItem[];
@@ -0,0 +1,34 @@
1
+ import { mapChecklistItem, mapHeading, mapProject, mapTodo, } from "../model/mappers.js";
2
+ import { decodeRecurrenceRule } from "../model/recurrence.js";
3
+ import { fetchChecklistRows, fetchTagsForTasks, fetchTaskByUuid, makeRefResolver, } from "./queries.js";
4
+ import { inheritedTagsFor } from "./tags.js";
5
+ export function byUuid(db, uuid) {
6
+ const row = fetchTaskByUuid(db, uuid);
7
+ if (!row)
8
+ return null;
9
+ return materializeOne(db, row);
10
+ }
11
+ function materializeOne(db, row) {
12
+ const refs = makeRefResolver(db);
13
+ if (row.type === 2)
14
+ return mapHeading(row, refs);
15
+ const tags = fetchTagsForTasks(db, [row.uuid]).get(row.uuid) ?? [];
16
+ const entity = row.type === 1 ? mapProject(row, refs, tags) : mapTodo(row, refs, tags);
17
+ entity.inheritedTags = inheritedTagsFor(db, row);
18
+ if (entity.type === "to-do")
19
+ entity.checklist = checklistFor(db, row.uuid);
20
+ if (entity.repeating.isTemplate && row.rt1_recurrenceRule !== null) {
21
+ try {
22
+ entity.repeating.rule = decodeRecurrenceRule(row.rt1_recurrenceRule);
23
+ }
24
+ catch {
25
+ // Unknown rule schema (future Things build) — surface the template
26
+ // without a decoded rule rather than failing the whole read.
27
+ }
28
+ }
29
+ return entity;
30
+ }
31
+ export function checklistFor(db, taskUuid) {
32
+ return fetchChecklistRows(db, taskUuid).map(mapChecklistItem);
33
+ }
34
+ //# sourceMappingURL=detail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detail.js","sourceRoot":"","sources":["../../src/read/detail.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,OAAO,GAER,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,UAAU,MAAM,CAAC,EAAgB,EAAE,IAAY;IACnD,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,cAAc,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,cAAc,CAAC,EAAgB,EAAE,GAAY;IACpD,MAAM,IAAI,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;IACjC,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,iBAAiB,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACnE,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACvF,MAAM,CAAC,aAAa,GAAG,gBAAgB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACjD,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;QAAE,MAAM,CAAC,SAAS,GAAG,YAAY,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3E,IAAI,MAAM,CAAC,SAAS,CAAC,UAAU,IAAI,GAAG,CAAC,kBAAkB,KAAK,IAAI,EAAE,CAAC;QACnE,IAAI,CAAC;YACH,MAAM,CAAC,SAAS,CAAC,IAAI,GAAG,oBAAoB,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QACvE,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;YACnE,6DAA6D;QAC/D,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAgB,EAAE,QAAgB;IAC7D,OAAO,kBAAkB,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Composite project view mirroring the native UI (validated T17 + "later
3
+ * items" findings): active items, headings with their children, later
4
+ * (scheduled by date / repeating templates / someday), logged, trashed.
5
+ *
6
+ * Child membership: `project = ? OR heading IN (project's headings)` — the
7
+ * DB invariant is that headed to-dos have project = NULL (atlas §TMTask).
8
+ * This is the dedup-safe alternative to things.py's include_items.
9
+ */
10
+ import type { DatabaseSync } from "node:sqlite";
11
+ import type { Heading, IsoDateGroup, Project, Todo } from "../model/entities.ts";
12
+ export interface ProjectView {
13
+ project: Project;
14
+ /** Open, unscheduled/current children not under a heading, by index. */
15
+ active: Todo[];
16
+ /** Headings in project order, each with its open children. */
17
+ headings: Array<{
18
+ heading: Heading;
19
+ items: Todo[];
20
+ }>;
21
+ later: {
22
+ /** Future-dated children grouped by date ascending. */
23
+ scheduled: IsoDateGroup<Todo>[];
24
+ /** Repeating template rows owned by this project (invisible in list views). */
25
+ repeating: Todo[];
26
+ someday: Todo[];
27
+ };
28
+ logged: Todo[];
29
+ trashed: Todo[];
30
+ }
31
+ export declare class ProjectNotFoundError extends Error {
32
+ constructor(uuid: string);
33
+ }
34
+ export declare function projectView(db: DatabaseSync, uuid: string, now?: Date): ProjectView;
@@ -0,0 +1,87 @@
1
+ import { encodePackedDate, localToday, decodePackedDate } from "../model/dates.js";
2
+ import { mapHeading, mapProject, mapTodo } from "../model/mappers.js";
3
+ import { fetchTagsForTasks, fetchTaskByUuid, fetchTaskRows, makeRefResolver } from "./queries.js";
4
+ export class ProjectNotFoundError extends Error {
5
+ constructor(uuid) {
6
+ super(`no project with uuid ${uuid}`);
7
+ this.name = "ProjectNotFoundError";
8
+ }
9
+ }
10
+ export function projectView(db, uuid, now) {
11
+ const projectRow = fetchTaskByUuid(db, uuid);
12
+ if (!projectRow || projectRow.type !== 1)
13
+ throw new ProjectNotFoundError(uuid);
14
+ const refs = makeRefResolver(db);
15
+ const tagsOf = (rows) => fetchTagsForTasks(db, rows.map((r) => r.uuid));
16
+ const projectTags = tagsOf([projectRow]);
17
+ const project = mapProject(projectRow, refs, projectTags.get(projectRow.uuid) ?? []);
18
+ const headingRows = fetchTaskRows(db, `t.type = 2 AND t.project = ? AND t.trashed = 0 ORDER BY t."index" ASC`, [uuid]);
19
+ const headings = headingRows.map((h) => mapHeading(h, refs));
20
+ const childRows = fetchTaskRows(db, `t.type = 0 AND (t.project = ? OR t.heading IN (
21
+ SELECT uuid FROM TMTask WHERE type = 2 AND project = ?
22
+ ))
23
+ ORDER BY t."index" ASC`, [uuid, uuid]);
24
+ const childTags = tagsOf(childRows);
25
+ const todos = childRows.map((r) => ({
26
+ row: r,
27
+ todo: mapTodo(r, refs, childTags.get(r.uuid) ?? []),
28
+ }));
29
+ const packedToday = encodePackedDate(localToday(now));
30
+ const active = [];
31
+ const byHeading = new Map();
32
+ const scheduledRows = [];
33
+ const repeating = [];
34
+ const someday = [];
35
+ const logged = [];
36
+ const trashed = [];
37
+ for (const { row, todo } of todos) {
38
+ if (row.trashed === 1) {
39
+ trashed.push(todo);
40
+ continue;
41
+ }
42
+ if (todo.repeating.isTemplate) {
43
+ repeating.push(todo);
44
+ continue;
45
+ }
46
+ if (row.status !== 0) {
47
+ logged.push(todo);
48
+ continue;
49
+ }
50
+ if (row.start === 2 && row.startDate === null) {
51
+ someday.push(todo);
52
+ continue;
53
+ }
54
+ if (row.startDate !== null && row.startDate > packedToday) {
55
+ scheduledRows.push({ date: decodePackedDate(row.startDate) ?? "", todo });
56
+ continue;
57
+ }
58
+ if (row.heading) {
59
+ const list = byHeading.get(row.heading) ?? [];
60
+ list.push(todo);
61
+ byHeading.set(row.heading, list);
62
+ continue;
63
+ }
64
+ active.push(todo);
65
+ }
66
+ logged.sort((a, b) => (b.stopped?.getTime() ?? 0) - (a.stopped?.getTime() ?? 0));
67
+ const scheduled = [];
68
+ for (const { date, todo } of scheduledRows.sort((a, b) => a.date.localeCompare(b.date))) {
69
+ const last = scheduled[scheduled.length - 1];
70
+ if (last && last.date === date)
71
+ last.items.push(todo);
72
+ else
73
+ scheduled.push({ date, items: [todo] });
74
+ }
75
+ return {
76
+ project,
77
+ active,
78
+ headings: headings.map((heading) => ({
79
+ heading,
80
+ items: byHeading.get(heading.uuid) ?? [],
81
+ })),
82
+ later: { scheduled, repeating, someday },
83
+ logged,
84
+ trashed,
85
+ };
86
+ }
87
+ //# sourceMappingURL=project-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-view.js","sourceRoot":"","sources":["../../src/read/project-view.ts"],"names":[],"mappings":"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"}