@todu/engine 0.1.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 (57) hide show
  1. package/dist/change-observer.d.ts +15 -0
  2. package/dist/change-observer.d.ts.map +1 -0
  3. package/dist/change-observer.js +64 -0
  4. package/dist/change-observer.js.map +1 -0
  5. package/dist/habits.d.ts +6 -0
  6. package/dist/habits.d.ts.map +1 -0
  7. package/dist/habits.js +397 -0
  8. package/dist/habits.js.map +1 -0
  9. package/dist/index.d.ts +25 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +104 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/labels.d.ts +5 -0
  14. package/dist/labels.d.ts.map +1 -0
  15. package/dist/labels.js +113 -0
  16. package/dist/labels.js.map +1 -0
  17. package/dist/notes.d.ts +5 -0
  18. package/dist/notes.d.ts.map +1 -0
  19. package/dist/notes.js +163 -0
  20. package/dist/notes.js.map +1 -0
  21. package/dist/projects.d.ts +5 -0
  22. package/dist/projects.d.ts.map +1 -0
  23. package/dist/projects.js +117 -0
  24. package/dist/projects.js.map +1 -0
  25. package/dist/recurring.d.ts +23 -0
  26. package/dist/recurring.d.ts.map +1 -0
  27. package/dist/recurring.js +372 -0
  28. package/dist/recurring.js.map +1 -0
  29. package/dist/schedule.d.ts +38 -0
  30. package/dist/schedule.d.ts.map +1 -0
  31. package/dist/schedule.js +330 -0
  32. package/dist/schedule.js.map +1 -0
  33. package/dist/scheduling.d.ts +62 -0
  34. package/dist/scheduling.d.ts.map +1 -0
  35. package/dist/scheduling.js +58 -0
  36. package/dist/scheduling.js.map +1 -0
  37. package/dist/storage.d.ts +34 -0
  38. package/dist/storage.d.ts.map +1 -0
  39. package/dist/storage.js +204 -0
  40. package/dist/storage.js.map +1 -0
  41. package/dist/sync-client.d.ts +16 -0
  42. package/dist/sync-client.d.ts.map +1 -0
  43. package/dist/sync-client.js +54 -0
  44. package/dist/sync-client.js.map +1 -0
  45. package/dist/sync-server.d.ts +12 -0
  46. package/dist/sync-server.d.ts.map +1 -0
  47. package/dist/sync-server.js +21 -0
  48. package/dist/sync-server.js.map +1 -0
  49. package/dist/tasks.d.ts +18 -0
  50. package/dist/tasks.d.ts.map +1 -0
  51. package/dist/tasks.js +396 -0
  52. package/dist/tasks.js.map +1 -0
  53. package/dist/todu.d.ts +115 -0
  54. package/dist/todu.d.ts.map +1 -0
  55. package/dist/todu.js +76 -0
  56. package/dist/todu.js.map +1 -0
  57. package/package.json +52 -0
@@ -0,0 +1,15 @@
1
+ import type { Repo } from "@automerge/automerge-repo";
2
+ /**
3
+ * Observe changes across all documents in a Repo.
4
+ *
5
+ * Subscribes to "change" events on every existing document handle and
6
+ * automatically subscribes to new documents as they are created or
7
+ * discovered (via the Repo "document" event).
8
+ *
9
+ * Changes are coalesced: multiple document changes within the same
10
+ * microtask batch fire the callback only once.
11
+ *
12
+ * @returns A cleanup function that removes all listeners.
13
+ */
14
+ export declare function observeAllChanges(repo: Repo, callback: () => void): () => void;
15
+ //# sourceMappingURL=change-observer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-observer.d.ts","sourceRoot":"","sources":["../src/change-observer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAyB,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAE7E;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAwD9E"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Observe changes across all documents in a Repo.
3
+ *
4
+ * Subscribes to "change" events on every existing document handle and
5
+ * automatically subscribes to new documents as they are created or
6
+ * discovered (via the Repo "document" event).
7
+ *
8
+ * Changes are coalesced: multiple document changes within the same
9
+ * microtask batch fire the callback only once.
10
+ *
11
+ * @returns A cleanup function that removes all listeners.
12
+ */
13
+ export function observeAllChanges(repo, callback) {
14
+ const observed = new Set();
15
+ let pending = false;
16
+ let pollTimer = null;
17
+ function coalesced() {
18
+ if (!pending) {
19
+ pending = true;
20
+ queueMicrotask(() => {
21
+ pending = false;
22
+ callback();
23
+ });
24
+ }
25
+ }
26
+ function observe(handle) {
27
+ if (observed.has(handle.documentId))
28
+ return;
29
+ observed.add(handle.documentId);
30
+ handle.on("change", coalesced);
31
+ }
32
+ /** Scan repo.handles for any new unobserved handles. */
33
+ function scanForNewHandles() {
34
+ for (const handle of Object.values(repo.handles)) {
35
+ observe(handle);
36
+ }
37
+ }
38
+ // Subscribe to all existing document handles
39
+ scanForNewHandles();
40
+ // The Repo "document" event may not fire in all versions of automerge-repo,
41
+ // so we also poll for new handles periodically. The poll is cheap — just
42
+ // iterating a map and checking a Set.
43
+ function onDocument({ handle }) {
44
+ observe(handle);
45
+ }
46
+ repo.on("document", onDocument);
47
+ // Poll every 2 seconds for new document handles that weren't caught by the event
48
+ pollTimer = setInterval(scanForNewHandles, 2000);
49
+ // Cleanup: remove all listeners
50
+ return () => {
51
+ repo.off("document", onDocument);
52
+ if (pollTimer !== null) {
53
+ clearInterval(pollTimer);
54
+ pollTimer = null;
55
+ }
56
+ for (const handle of Object.values(repo.handles)) {
57
+ if (observed.has(handle.documentId)) {
58
+ handle.off("change", coalesced);
59
+ }
60
+ }
61
+ observed.clear();
62
+ };
63
+ }
64
+ //# sourceMappingURL=change-observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-observer.js","sourceRoot":"","sources":["../src/change-observer.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAU,EAAE,QAAoB,EAAc;IAC9E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAc,CAAC;IACvC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,SAAS,GAA0C,IAAI,CAAC;IAE5D,SAAS,SAAS,GAAS;QACzB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,IAAI,CAAC;YACf,cAAc,CAAC,GAAG,EAAE,CAAC;gBACnB,OAAO,GAAG,KAAK,CAAC;gBAChB,QAAQ,EAAE,CAAC;YAAA,CACZ,CAAC,CAAC;QACL,CAAC;IAAA,CACF;IAED,SAAS,OAAO,CAAC,MAA0B,EAAQ;QACjD,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC;YAAE,OAAO;QAC5C,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAChC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAAA,CAChC;IAED,wDAAwD;IACxD,SAAS,iBAAiB,GAAS;QACjC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACjD,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC;IAAA,CACF;IAED,6CAA6C;IAC7C,iBAAiB,EAAE,CAAC;IAEpB,4EAA4E;IAC5E,2EAAyE;IACzE,sCAAsC;IACtC,SAAS,UAAU,CAAC,EAAE,MAAM,EAAkC,EAAQ;QACpE,OAAO,CAAC,MAAM,CAAC,CAAC;IAAA,CACjB;IACD,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAEhC,iFAAiF;IACjF,SAAS,GAAG,WAAW,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;IAEjD,gCAAgC;IAChC,OAAO,GAAG,EAAE,CAAC;QACX,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACjC,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,aAAa,CAAC,SAAS,CAAC,CAAC;YACzB,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACjD,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QACD,QAAQ,CAAC,KAAK,EAAE,CAAC;IAAA,CAClB,CAAC;AAAA,CACH"}
@@ -0,0 +1,6 @@
1
+ import type { DocHandle, Repo } from "@automerge/automerge-repo";
2
+ import { type CatalogDocument } from "@todu/core";
3
+ import type { HabitNamespace } from "./todu.js";
4
+ export declare function createHabitNamespace(catalog: DocHandle<CatalogDocument>, repo: Repo): HabitNamespace;
5
+ export declare function registerHabitProcessor(_catalog: DocHandle<CatalogDocument>, _repo: Repo): void;
6
+ //# sourceMappingURL=habits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"habits.d.ts","sourceRoot":"","sources":["../src/habits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAc,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EACL,KAAK,eAAe,EAmBrB,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAqBhD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC,EACnC,IAAI,EAAE,IAAI,GACT,cAAc,CA0XhB;AAMD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,SAAS,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,GAAG,IAAI,CA6B9F"}
package/dist/habits.js ADDED
@@ -0,0 +1,397 @@
1
+ import { createHabitId, createHabitLogDocument, err, notFound, ok, validateCreateHabitInput, validateUpdateHabitInput, validationError, } from "@todu/core";
2
+ import { isScheduledDate, nextOccurrence, todayInTimezone } from "./schedule.js";
3
+ import { registerProcessor } from "./scheduling.js";
4
+ // ============================================================================
5
+ // Helpers
6
+ // ============================================================================
7
+ function generateHabitId() {
8
+ const hex = [...crypto.getRandomValues(new Uint8Array(4))]
9
+ .map((b) => b.toString(16).padStart(2, "0"))
10
+ .join("");
11
+ return createHabitId(`hab-${hex}`);
12
+ }
13
+ function cloneHabit(h) {
14
+ return JSON.parse(JSON.stringify(h));
15
+ }
16
+ // ============================================================================
17
+ // Habit namespace factory
18
+ // ============================================================================
19
+ export function createHabitNamespace(catalog, repo) {
20
+ async function getLogHandle(habitId) {
21
+ const doc = catalog.doc();
22
+ if (!doc)
23
+ return null;
24
+ const docId = doc.habitLogDocIds[habitId];
25
+ if (!docId)
26
+ return null;
27
+ return repo.find(docId);
28
+ }
29
+ async function getOrCreateLogHandle(habitId) {
30
+ const existing = await getLogHandle(habitId);
31
+ if (existing)
32
+ return existing;
33
+ const handle = repo.create();
34
+ const empty = createHabitLogDocument(habitId);
35
+ handle.change((doc) => {
36
+ doc.habitId = empty.habitId;
37
+ doc.entries = empty.entries;
38
+ });
39
+ catalog.change((doc) => {
40
+ doc.habitLogDocIds[habitId] = handle.documentId;
41
+ });
42
+ return handle;
43
+ }
44
+ return {
45
+ async create(input) {
46
+ const validationErr = validateCreateHabitInput(input);
47
+ if (validationErr)
48
+ return err(validationErr);
49
+ const id = generateHabitId();
50
+ const now = new Date().toISOString();
51
+ const nextDue = nextOccurrence(input.schedule, input.startDate, input.timezone, input.startDate, input.endDate);
52
+ if (!nextDue) {
53
+ return err(validationError("schedule", "Schedule produces no occurrences from start date"));
54
+ }
55
+ const habit = {
56
+ id,
57
+ title: input.title.trim(),
58
+ description: input.description,
59
+ schedule: input.schedule,
60
+ timezone: input.timezone,
61
+ startDate: input.startDate,
62
+ endDate: input.endDate,
63
+ nextDue,
64
+ paused: false,
65
+ createdAt: now,
66
+ updatedAt: now,
67
+ };
68
+ // Create the HabitLogDocument
69
+ await getOrCreateLogHandle(id);
70
+ catalog.change((doc) => {
71
+ // Strip undefined for Automerge
72
+ const entry = { ...habit };
73
+ for (const key of Object.keys(entry)) {
74
+ if (entry[key] === undefined)
75
+ delete entry[key];
76
+ }
77
+ doc.habits.push(entry);
78
+ });
79
+ return ok(cloneHabit(habit));
80
+ },
81
+ async list(filter) {
82
+ const doc = catalog.doc();
83
+ if (!doc)
84
+ return ok([]);
85
+ let habits = [...doc.habits];
86
+ if (filter?.paused !== undefined) {
87
+ habits = habits.filter((h) => h.paused === filter.paused);
88
+ }
89
+ if (filter?.search) {
90
+ const lowerQuery = filter.search.toLowerCase();
91
+ habits = habits.filter((h) => h.title.toLowerCase().includes(lowerQuery));
92
+ }
93
+ if (filter?.checkedToday !== undefined) {
94
+ const checked = [];
95
+ for (const h of habits) {
96
+ const logHandle = await getLogHandle(h.id);
97
+ const logDoc = logHandle?.doc();
98
+ const today = todayInTimezone(h.timezone);
99
+ const completedToday = logDoc?.entries[today]?.completed === true;
100
+ if (completedToday === filter.checkedToday) {
101
+ checked.push(h);
102
+ }
103
+ }
104
+ habits = checked;
105
+ }
106
+ return ok(habits.map(cloneHabit));
107
+ },
108
+ async get(id) {
109
+ const doc = catalog.doc();
110
+ if (!doc)
111
+ return err(notFound("habit", id));
112
+ const habit = doc.habits.find((h) => h.id === id);
113
+ if (!habit)
114
+ return err(notFound("habit", id));
115
+ return ok(cloneHabit(habit));
116
+ },
117
+ async update(id, input) {
118
+ const validationErr = validateUpdateHabitInput(input);
119
+ if (validationErr)
120
+ return err(validationErr);
121
+ const doc = catalog.doc();
122
+ if (!doc)
123
+ return err(notFound("habit", id));
124
+ const index = doc.habits.findIndex((h) => h.id === id);
125
+ if (index === -1)
126
+ return err(notFound("habit", id));
127
+ const scheduleChanged = input.schedule !== undefined || input.timezone !== undefined;
128
+ catalog.change((doc) => {
129
+ const h = doc.habits[index];
130
+ if (input.title !== undefined)
131
+ h.title = input.title.trim();
132
+ if (input.description !== undefined)
133
+ h.description = input.description;
134
+ if (input.schedule !== undefined)
135
+ h.schedule = input.schedule;
136
+ if (input.timezone !== undefined)
137
+ h.timezone = input.timezone;
138
+ if (input.endDate !== undefined)
139
+ h.endDate = input.endDate;
140
+ h.updatedAt = new Date().toISOString();
141
+ if (scheduleChanged) {
142
+ const next = nextOccurrence(h.schedule, h.startDate, h.timezone, h.nextDue, h.endDate);
143
+ if (next)
144
+ h.nextDue = next;
145
+ }
146
+ });
147
+ const updated = catalog.doc().habits[index];
148
+ return ok(cloneHabit(updated));
149
+ },
150
+ async delete(id) {
151
+ const doc = catalog.doc();
152
+ if (!doc)
153
+ return err(notFound("habit", id));
154
+ const index = doc.habits.findIndex((h) => h.id === id);
155
+ if (index === -1)
156
+ return err(notFound("habit", id));
157
+ catalog.change((doc) => {
158
+ doc.habits.splice(index, 1);
159
+ delete doc.habitLogDocIds[id];
160
+ });
161
+ return ok(undefined);
162
+ },
163
+ async pause(id) {
164
+ const doc = catalog.doc();
165
+ if (!doc)
166
+ return err(notFound("habit", id));
167
+ const index = doc.habits.findIndex((h) => h.id === id);
168
+ if (index === -1)
169
+ return err(notFound("habit", id));
170
+ catalog.change((doc) => {
171
+ doc.habits[index].paused = true;
172
+ doc.habits[index].updatedAt = new Date().toISOString();
173
+ });
174
+ return ok(cloneHabit(catalog.doc().habits[index]));
175
+ },
176
+ async resume(id) {
177
+ const doc = catalog.doc();
178
+ if (!doc)
179
+ return err(notFound("habit", id));
180
+ const index = doc.habits.findIndex((h) => h.id === id);
181
+ if (index === -1)
182
+ return err(notFound("habit", id));
183
+ catalog.change((doc) => {
184
+ const h = doc.habits[index];
185
+ h.paused = false;
186
+ h.updatedAt = new Date().toISOString();
187
+ // Recalculate nextDue from today
188
+ const today = todayInTimezone(h.timezone);
189
+ const next = nextOccurrence(h.schedule, h.startDate, h.timezone, today, h.endDate);
190
+ if (next)
191
+ h.nextDue = next;
192
+ });
193
+ return ok(cloneHabit(catalog.doc().habits[index]));
194
+ },
195
+ async check(id) {
196
+ const doc = catalog.doc();
197
+ if (!doc)
198
+ return err(notFound("habit", id));
199
+ const habit = doc.habits.find((h) => h.id === id);
200
+ if (!habit)
201
+ return err(notFound("habit", id));
202
+ const today = todayInTimezone(habit.timezone);
203
+ const logHandle = await getLogHandle(id);
204
+ if (!logHandle)
205
+ return err(notFound("habit log", id));
206
+ const logDoc = logHandle.doc();
207
+ // If already checked today, return the existing entry (idempotent)
208
+ if (logDoc?.entries[today]?.completed) {
209
+ const entry = logDoc.entries[today];
210
+ return ok({ date: entry.date, completed: entry.completed, checkedAt: entry.checkedAt });
211
+ }
212
+ const entry = {
213
+ date: today,
214
+ completed: true,
215
+ checkedAt: new Date().toISOString(),
216
+ };
217
+ logHandle.change((doc) => {
218
+ doc.entries[today] = entry;
219
+ });
220
+ return ok(entry);
221
+ },
222
+ async uncheck(id) {
223
+ const doc = catalog.doc();
224
+ if (!doc)
225
+ return err(notFound("habit", id));
226
+ const habit = doc.habits.find((h) => h.id === id);
227
+ if (!habit)
228
+ return err(notFound("habit", id));
229
+ const today = todayInTimezone(habit.timezone);
230
+ const logHandle = await getLogHandle(id);
231
+ if (!logHandle)
232
+ return err(notFound("habit log", id));
233
+ const logDoc = logHandle.doc();
234
+ // If not checked today, return success (idempotent)
235
+ if (!logDoc?.entries[today])
236
+ return ok(undefined);
237
+ logHandle.change((doc) => {
238
+ delete doc.entries[today];
239
+ });
240
+ return ok(undefined);
241
+ },
242
+ async streak(id) {
243
+ const doc = catalog.doc();
244
+ if (!doc)
245
+ return err(notFound("habit", id));
246
+ const habit = doc.habits.find((h) => h.id === id);
247
+ if (!habit)
248
+ return err(notFound("habit", id));
249
+ const logHandle = await getLogHandle(id);
250
+ if (!logHandle)
251
+ return err(notFound("habit log", id));
252
+ const logDoc = logHandle.doc();
253
+ const entries = logDoc?.entries ?? {};
254
+ const today = todayInTimezone(habit.timezone);
255
+ const completedToday = entries[today]?.completed === true;
256
+ // Count total check-ins
257
+ let totalCheckins = 0;
258
+ for (const entry of Object.values(entries)) {
259
+ if (entry.completed)
260
+ totalCheckins++;
261
+ }
262
+ // Compute current streak: count backwards from today through scheduled dates
263
+ let current = 0;
264
+ let checkDate = today;
265
+ while (true) {
266
+ const isScheduled = isScheduledDate(habit.schedule, habit.startDate, habit.timezone, checkDate, habit.endDate);
267
+ if (isScheduled) {
268
+ if (entries[checkDate]?.completed) {
269
+ current++;
270
+ }
271
+ else {
272
+ break;
273
+ }
274
+ }
275
+ checkDate = shiftDate(checkDate, -1);
276
+ // Don't go before start date
277
+ if (checkDate < habit.startDate)
278
+ break;
279
+ }
280
+ // Compute longest streak: scan all entries sorted by date
281
+ const sortedDates = Object.keys(entries)
282
+ .filter((d) => entries[d].completed)
283
+ .sort();
284
+ let longest = 0;
285
+ let streak = 0;
286
+ let prevScheduledDate = null;
287
+ for (const date of sortedDates) {
288
+ if (!isScheduledDate(habit.schedule, habit.startDate, habit.timezone, date, habit.endDate)) {
289
+ continue;
290
+ }
291
+ if (prevScheduledDate === null) {
292
+ streak = 1;
293
+ }
294
+ else {
295
+ // Check if there's an unscheduled gap
296
+ const hasGap = hasUncompletedScheduledDateBetween(habit.schedule, habit.startDate, habit.timezone, prevScheduledDate, date, entries, habit.endDate);
297
+ if (hasGap) {
298
+ streak = 1;
299
+ }
300
+ else {
301
+ streak++;
302
+ }
303
+ }
304
+ if (streak > longest)
305
+ longest = streak;
306
+ prevScheduledDate = date;
307
+ }
308
+ return ok({ current, longest, completedToday, totalCheckins });
309
+ },
310
+ async history(id, days = 30) {
311
+ const doc = catalog.doc();
312
+ if (!doc)
313
+ return err(notFound("habit", id));
314
+ const habit = doc.habits.find((h) => h.id === id);
315
+ if (!habit)
316
+ return err(notFound("habit", id));
317
+ const logHandle = await getLogHandle(id);
318
+ if (!logHandle)
319
+ return err(notFound("habit log", id));
320
+ const logDoc = logHandle.doc();
321
+ const entries = logDoc?.entries ?? {};
322
+ const today = todayInTimezone(habit.timezone);
323
+ const result = [];
324
+ for (let i = 0; i < days; i++) {
325
+ const date = shiftDate(today, -i);
326
+ if (date < habit.startDate)
327
+ break;
328
+ const scheduled = isScheduledDate(habit.schedule, habit.startDate, habit.timezone, date, habit.endDate);
329
+ if (scheduled) {
330
+ result.push({
331
+ date,
332
+ scheduled: true,
333
+ completed: entries[date]?.completed === true,
334
+ });
335
+ }
336
+ }
337
+ return ok(result);
338
+ },
339
+ };
340
+ }
341
+ // ============================================================================
342
+ // Habit processor for processTemplates()
343
+ // ============================================================================
344
+ export function registerHabitProcessor(_catalog, _repo) {
345
+ registerProcessor("habit", async (context) => {
346
+ const doc = context.catalog.doc();
347
+ if (!doc?.habits)
348
+ return 0;
349
+ let processed = 0;
350
+ for (let i = 0; i < doc.habits.length; i++) {
351
+ const habit = doc.habits[i];
352
+ if (habit.paused)
353
+ continue;
354
+ const today = todayInTimezone(habit.timezone);
355
+ if (habit.nextDue > today)
356
+ continue;
357
+ // Advance nextDue to today or next scheduled date
358
+ context.catalog.change((d) => {
359
+ const h = d.habits[i];
360
+ const next = nextOccurrence(h.schedule, h.startDate, h.timezone, today, h.endDate);
361
+ if (next) {
362
+ h.nextDue = next;
363
+ }
364
+ h.updatedAt = new Date().toISOString();
365
+ });
366
+ processed++;
367
+ }
368
+ return processed;
369
+ });
370
+ }
371
+ // ============================================================================
372
+ // Date helpers
373
+ // ============================================================================
374
+ function shiftDate(dateStr, days) {
375
+ const [y, m, d] = dateStr.split("-").map(Number);
376
+ const date = new Date(Date.UTC(y, m - 1, d));
377
+ date.setUTCDate(date.getUTCDate() + days);
378
+ const yy = date.getUTCFullYear();
379
+ const mm = String(date.getUTCMonth() + 1).padStart(2, "0");
380
+ const dd = String(date.getUTCDate()).padStart(2, "0");
381
+ return `${yy}-${mm}-${dd}`;
382
+ }
383
+ /**
384
+ * Check if there's any scheduled date between two dates that lacks a completed entry.
385
+ */
386
+ function hasUncompletedScheduledDateBetween(schedule, startDate, timezone, from, to, entries, endDate) {
387
+ let date = shiftDate(from, 1);
388
+ while (date < to) {
389
+ if (isScheduledDate(schedule, startDate, timezone, date, endDate)) {
390
+ if (!entries[date]?.completed)
391
+ return true;
392
+ }
393
+ date = shiftDate(date, 1);
394
+ }
395
+ return false;
396
+ }
397
+ //# sourceMappingURL=habits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"habits.js","sourceRoot":"","sources":["../src/habits.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { type Todu, type ToduConfig } from "./todu.js";
2
+ export type { UpcomingOccurrence } from "./recurring.js";
3
+ export { describeSchedule, isScheduledDate, nextOccurrence, nextOccurrences, todayInTimezone, } from "./schedule.js";
4
+ export type { ProcessingContext, SchedulableItem, TemplateProcessor } from "./scheduling.js";
5
+ export { clearProcessors, getRegisteredProcessors, registerProcessor } from "./scheduling.js";
6
+ export type { Storage } from "./storage.js";
7
+ export { isSyncServerAvailable } from "./sync-client.js";
8
+ export { DEFAULT_SYNC_PORT } from "./sync-server.js";
9
+ export type { HabitNamespace, LabelNamespace, LocalSyncMode, NoteNamespace, ProjectNamespace, RecurringNamespace, RemoteSyncState, SyncStatus, TaskNamespace, Todu, ToduConfig, } from "./todu.js";
10
+ /**
11
+ * Create a Todu SDK instance.
12
+ *
13
+ * Initializes Automerge storage, loads or creates the catalog document,
14
+ * processes any due recurring templates/habits, and returns the SDK
15
+ * with all operation namespaces.
16
+ *
17
+ * Sync modes:
18
+ * - `syncServer: true` — Start a WebSocket sync server. Other instances
19
+ * (CLI, other devices) can connect and sync. Used by Electron.
20
+ * - `syncClient: true` — Connect to a running sync server. Changes
21
+ * propagate bidirectionally via Automerge sync protocol. Used by CLI.
22
+ * - Neither — Standalone, no sync. Used by tests.
23
+ */
24
+ export declare function createTodu(config: Pick<ToduConfig, "storagePath"> & Partial<Omit<ToduConfig, "storagePath">>): Promise<Todu>;
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAIL,KAAK,IAAI,EACT,KAAK,UAAU,EAChB,MAAM,WAAW,CAAC;AAEnB,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,eAAe,EACf,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC9F,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,YAAY,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,aAAa,EACb,IAAI,EACJ,UAAU,GACX,MAAM,WAAW,CAAC;AAEnB;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC,GACjF,OAAO,CAAC,IAAI,CAAC,CA+Ef"}
package/dist/index.js ADDED
@@ -0,0 +1,104 @@
1
+ import { observeAllChanges } from "./change-observer.js";
2
+ import { createHabitNamespace, registerHabitProcessor } from "./habits.js";
3
+ import { createLabelNamespace } from "./labels.js";
4
+ import { createNoteNamespace } from "./notes.js";
5
+ import { createProjectNamespace } from "./projects.js";
6
+ import { createRecurringNamespace, registerRecurringProcessor } from "./recurring.js";
7
+ import { processTemplates } from "./scheduling.js";
8
+ import { initEphemeralStorage, initStorage } from "./storage.js";
9
+ import { connectSyncClient } from "./sync-client.js";
10
+ import { startSyncServer } from "./sync-server.js";
11
+ import { createTaskNamespace } from "./tasks.js";
12
+ import { createStubNamespaces, } from "./todu.js";
13
+ // Re-export schedule utilities for consumers
14
+ export { describeSchedule, isScheduledDate, nextOccurrence, nextOccurrences, todayInTimezone, } from "./schedule.js";
15
+ export { clearProcessors, getRegisteredProcessors, registerProcessor } from "./scheduling.js";
16
+ export { isSyncServerAvailable } from "./sync-client.js";
17
+ export { DEFAULT_SYNC_PORT } from "./sync-server.js";
18
+ /**
19
+ * Create a Todu SDK instance.
20
+ *
21
+ * Initializes Automerge storage, loads or creates the catalog document,
22
+ * processes any due recurring templates/habits, and returns the SDK
23
+ * with all operation namespaces.
24
+ *
25
+ * Sync modes:
26
+ * - `syncServer: true` — Start a WebSocket sync server. Other instances
27
+ * (CLI, other devices) can connect and sync. Used by Electron.
28
+ * - `syncClient: true` — Connect to a running sync server. Changes
29
+ * propagate bidirectionally via Automerge sync protocol. Used by CLI.
30
+ * - Neither — Standalone, no sync. Used by tests.
31
+ */
32
+ export async function createTodu(config) {
33
+ const resolvedConfig = {
34
+ storagePath: config.storagePath,
35
+ };
36
+ // Sync client mode: ephemeral in-memory repo that syncs with server
37
+ // Sync server mode: persistent repo that serves sync clients
38
+ // Standalone: persistent repo, no sync
39
+ let syncServer = null;
40
+ let storage;
41
+ if (config?.syncClient) {
42
+ // Mode 2: CLI as ephemeral sync client
43
+ // 1. Create ephemeral repo (no storage)
44
+ // 2. Connect sync adapter so the repo has a peer
45
+ // 3. Find catalog document — sync peer provides the data
46
+ const ephemeral = await initEphemeralStorage(resolvedConfig.storagePath);
47
+ const port = config.syncPort ?? 24377;
48
+ await connectSyncClient(ephemeral.repo, `ws://127.0.0.1:${port}`);
49
+ storage = await ephemeral.findCatalog();
50
+ }
51
+ else {
52
+ // Mode 1 (standalone) or Electron (sync server)
53
+ storage = await initStorage(resolvedConfig.storagePath);
54
+ if (config?.syncServer) {
55
+ syncServer = startSyncServer(storage.repo, config.syncPort);
56
+ }
57
+ }
58
+ // Register processors before processing
59
+ registerRecurringProcessor(storage.catalog, storage.repo);
60
+ registerHabitProcessor(storage.catalog, storage.repo);
61
+ // Process due templates/habits before returning the SDK.
62
+ // This is the "generate on access" pattern — every CLI invocation
63
+ // and Electron launch triggers template processing.
64
+ await processTemplates(storage.catalog);
65
+ // Determine local sync mode
66
+ const localMode = config?.syncClient
67
+ ? "ephemeral-client"
68
+ : config?.syncServer
69
+ ? "sync-server"
70
+ : "standalone";
71
+ const syncStatus = {
72
+ local: { mode: localMode },
73
+ // Remote multi-device sync is not yet implemented (phase 3)
74
+ remote: { state: "disconnected" },
75
+ };
76
+ const stubs = createStubNamespaces(resolvedConfig);
77
+ return {
78
+ ...stubs,
79
+ project: createProjectNamespace(storage.catalog),
80
+ task: createTaskNamespace(storage.catalog, storage.repo),
81
+ label: createLabelNamespace(storage.catalog, storage.repo),
82
+ note: createNoteNamespace(storage.catalog, storage.repo),
83
+ recurring: createRecurringNamespace(storage.catalog, storage.repo),
84
+ habit: createHabitNamespace(storage.catalog, storage.repo),
85
+ sync: {
86
+ status: () => syncStatus,
87
+ start: () => Promise.resolve(),
88
+ stop: () => Promise.resolve(),
89
+ },
90
+ onChange(callback) {
91
+ return observeAllChanges(storage.repo, callback);
92
+ },
93
+ async close() {
94
+ // repo.shutdown() handles disconnecting network adapters.
95
+ // We just need to close the WS server (if any) after shutdown.
96
+ await storage.close();
97
+ if (syncServer) {
98
+ await syncServer.close();
99
+ syncServer = null;
100
+ }
101
+ },
102
+ };
103
+ }
104
+ //# sourceMappingURL=index.js.map