@redjay/threads-core 1.1.0 → 1.2.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 (55) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +42 -42
  3. package/dist/domain/agenda.d.ts +31 -0
  4. package/dist/domain/agenda.d.ts.map +1 -0
  5. package/dist/domain/agenda.js +60 -0
  6. package/dist/domain/agenda.js.map +1 -0
  7. package/dist/domain/batch.d.ts +67 -0
  8. package/dist/domain/batch.d.ts.map +1 -0
  9. package/dist/domain/batch.js +151 -0
  10. package/dist/domain/batch.js.map +1 -0
  11. package/dist/domain/clone.d.ts +31 -0
  12. package/dist/domain/clone.d.ts.map +1 -0
  13. package/dist/domain/clone.js +69 -0
  14. package/dist/domain/clone.js.map +1 -0
  15. package/dist/domain/index.d.ts +12 -0
  16. package/dist/domain/index.d.ts.map +1 -0
  17. package/dist/domain/index.js +30 -0
  18. package/dist/domain/index.js.map +1 -0
  19. package/dist/domain/merge.d.ts +25 -0
  20. package/dist/domain/merge.d.ts.map +1 -0
  21. package/dist/domain/merge.js +47 -0
  22. package/dist/domain/merge.js.map +1 -0
  23. package/dist/domain/overview.d.ts +41 -0
  24. package/dist/domain/overview.d.ts.map +1 -0
  25. package/dist/domain/overview.js +95 -0
  26. package/dist/domain/overview.js.map +1 -0
  27. package/dist/domain/progress.d.ts +28 -0
  28. package/dist/domain/progress.d.ts.map +1 -0
  29. package/dist/domain/progress.js +45 -0
  30. package/dist/domain/progress.js.map +1 -0
  31. package/dist/domain/scoring.d.ts +40 -0
  32. package/dist/domain/scoring.d.ts.map +1 -0
  33. package/dist/domain/scoring.js +64 -0
  34. package/dist/domain/scoring.js.map +1 -0
  35. package/dist/domain/search.d.ts +31 -0
  36. package/dist/domain/search.d.ts.map +1 -0
  37. package/dist/domain/search.js +59 -0
  38. package/dist/domain/search.js.map +1 -0
  39. package/dist/domain/sorting.d.ts +17 -0
  40. package/dist/domain/sorting.d.ts.map +1 -0
  41. package/dist/domain/sorting.js +32 -0
  42. package/dist/domain/sorting.js.map +1 -0
  43. package/dist/domain/time.d.ts +78 -0
  44. package/dist/domain/time.d.ts.map +1 -0
  45. package/dist/domain/time.js +146 -0
  46. package/dist/domain/time.js.map +1 -0
  47. package/dist/domain/timeline.d.ts +43 -0
  48. package/dist/domain/timeline.d.ts.map +1 -0
  49. package/dist/domain/timeline.js +65 -0
  50. package/dist/domain/timeline.js.map +1 -0
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +2 -0
  54. package/dist/index.js.map +1 -1
  55. package/package.json +62 -54
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /**
3
+ * Thread merging — progress, details, tags, dependencies.
4
+ *
5
+ * Pure functions — no storage dependencies.
6
+ * All functions take source and target arrays, return merged result.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.mergeProgress = mergeProgress;
10
+ exports.mergeDetails = mergeDetails;
11
+ exports.mergeTags = mergeTags;
12
+ exports.mergeDependencies = mergeDependencies;
13
+ /**
14
+ * Merge progress entries from target and source, sorted by timestamp.
15
+ */
16
+ function mergeProgress(target, source) {
17
+ return [...target, ...source].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
18
+ }
19
+ /**
20
+ * Merge details entries from target and source, sorted by timestamp.
21
+ */
22
+ function mergeDetails(target, source) {
23
+ return [...target, ...source].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
24
+ }
25
+ /**
26
+ * Merge tags as a union (no duplicates).
27
+ */
28
+ function mergeTags(target, source) {
29
+ const set = new Set([...target, ...source]);
30
+ return Array.from(set);
31
+ }
32
+ /**
33
+ * Merge dependencies as a union by threadId.
34
+ * If both have the same threadId, target's version wins.
35
+ */
36
+ function mergeDependencies(target, source) {
37
+ const byThreadId = new Map();
38
+ // Add source first so target can override
39
+ for (const dep of source) {
40
+ byThreadId.set(dep.threadId, dep);
41
+ }
42
+ for (const dep of target) {
43
+ byThreadId.set(dep.threadId, dep);
44
+ }
45
+ return Array.from(byThreadId.values());
46
+ }
47
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/domain/merge.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAOH,sCAIC;AAKD,oCAIC;AAKD,8BAGC;AAMD,8CAYC;AA1CD;;GAEG;AACH,SAAgB,aAAa,CAAC,MAAuB,EAAE,MAAuB;IAC5E,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,IAAI,CAChC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAC5E,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,YAAY,CAAC,MAAsB,EAAE,MAAsB;IACzE,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,IAAI,CAChC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAC5E,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,SAAS,CAAC,MAAgB,EAAE,MAAgB;IAC1D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;IAC5C,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,MAAoB,EAAE,MAAoB;IAC1E,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEjD,0CAA0C;IAC1C,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACpC,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Dashboard overview aggregation.
3
+ *
4
+ * Pure function — takes threads/groups arrays, returns computed overview data.
5
+ * Extracted from the inline logic in overview command's .action() handler.
6
+ */
7
+ import { Thread, Group } from '../models/types';
8
+ /** Thread with recent activity metadata. */
9
+ export interface RecentThread {
10
+ thread: Thread;
11
+ updateCount: number;
12
+ lastNote: string;
13
+ }
14
+ /** Computed overview sections. */
15
+ export interface OverviewSections {
16
+ hotThreads: Thread[];
17
+ recentThreads: RecentThread[];
18
+ coldThreads: Thread[];
19
+ statsByGroup: Map<string, {
20
+ count: number;
21
+ hot: number;
22
+ warm: number;
23
+ }>;
24
+ ungroupedStats: {
25
+ count: number;
26
+ hot: number;
27
+ warm: number;
28
+ };
29
+ statusCounts: Record<string, number>;
30
+ groupMap: Map<string, Group>;
31
+ }
32
+ /**
33
+ * Compute the full dashboard overview from raw data.
34
+ *
35
+ * @param threads - All threads
36
+ * @param groups - All groups
37
+ * @param daysThreshold - Days threshold for recent/cold classification (default 7)
38
+ * @param now - Current time (injectable for testing)
39
+ */
40
+ export declare function computeOverview(threads: Thread[], groups: Group[], daysThreshold?: number, now?: Date): OverviewSections;
41
+ //# sourceMappingURL=overview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overview.d.ts","sourceRoot":"","sources":["../../src/domain/overview.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAGhD,4CAA4C;AAC5C,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,kCAAkC;AAClC,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxE,cAAc,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,KAAK,EAAE,EACf,aAAa,GAAE,MAAU,EACzB,GAAG,GAAE,IAAiB,GACrB,gBAAgB,CA6ElB"}
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ /**
3
+ * Dashboard overview aggregation.
4
+ *
5
+ * Pure function — takes threads/groups arrays, returns computed overview data.
6
+ * Extracted from the inline logic in overview command's .action() handler.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.computeOverview = computeOverview;
10
+ const time_1 = require("./time");
11
+ /**
12
+ * Compute the full dashboard overview from raw data.
13
+ *
14
+ * @param threads - All threads
15
+ * @param groups - All groups
16
+ * @param daysThreshold - Days threshold for recent/cold classification (default 7)
17
+ * @param now - Current time (injectable for testing)
18
+ */
19
+ function computeOverview(threads, groups, daysThreshold = 7, now = new Date()) {
20
+ // Build group lookup
21
+ const groupMap = new Map();
22
+ groups.forEach(g => groupMap.set(g.id, g));
23
+ // Filter out archived for most sections
24
+ const activeThreads = threads.filter(t => t.status !== 'archived');
25
+ // Hot threads
26
+ const hotThreads = activeThreads.filter(t => t.temperature === 'hot');
27
+ // Recent activity
28
+ const recentThreads = [];
29
+ activeThreads.forEach(t => {
30
+ const lastDate = (0, time_1.getLastProgressDate)(t);
31
+ if (lastDate && (0, time_1.daysSince)(lastDate, now) <= daysThreshold) {
32
+ const recentUpdates = t.progress.filter(p => (0, time_1.daysSince)(p.timestamp, now) <= daysThreshold);
33
+ const lastNote = (0, time_1.getLastProgressNote)(t) || '';
34
+ recentThreads.push({ thread: t, updateCount: recentUpdates.length, lastNote });
35
+ }
36
+ });
37
+ recentThreads.sort((a, b) => {
38
+ const aDate = a.thread.lastUpdated || a.thread.modified;
39
+ const bDate = b.thread.lastUpdated || b.thread.modified;
40
+ return new Date(bDate).getTime() - new Date(aDate).getTime();
41
+ });
42
+ // Going cold: active threads with no updates in N+ days
43
+ const coldThreads = activeThreads.filter(t => {
44
+ if (t.status !== 'active')
45
+ return false;
46
+ const lastDate = (0, time_1.getLastProgressDate)(t);
47
+ if (!lastDate) {
48
+ return (0, time_1.daysSince)(t.createdAt, now) > daysThreshold;
49
+ }
50
+ return (0, time_1.daysSince)(lastDate, now) > daysThreshold;
51
+ });
52
+ // Stats by group
53
+ const statsByGroup = new Map();
54
+ const ungroupedStats = { count: 0, hot: 0, warm: 0 };
55
+ activeThreads.forEach(t => {
56
+ const isHot = t.temperature === 'hot';
57
+ const isWarm = t.temperature === 'warm';
58
+ if (t.groupId) {
59
+ if (!statsByGroup.has(t.groupId)) {
60
+ statsByGroup.set(t.groupId, { count: 0, hot: 0, warm: 0 });
61
+ }
62
+ const stats = statsByGroup.get(t.groupId);
63
+ stats.count++;
64
+ if (isHot)
65
+ stats.hot++;
66
+ if (isWarm)
67
+ stats.warm++;
68
+ }
69
+ else {
70
+ ungroupedStats.count++;
71
+ if (isHot)
72
+ ungroupedStats.hot++;
73
+ if (isWarm)
74
+ ungroupedStats.warm++;
75
+ }
76
+ });
77
+ // Status counts (across ALL threads, including archived)
78
+ const statusCounts = {
79
+ active: threads.filter(t => t.status === 'active').length,
80
+ paused: threads.filter(t => t.status === 'paused').length,
81
+ stopped: threads.filter(t => t.status === 'stopped').length,
82
+ completed: threads.filter(t => t.status === 'completed').length,
83
+ archived: threads.filter(t => t.status === 'archived').length,
84
+ };
85
+ return {
86
+ hotThreads,
87
+ recentThreads,
88
+ coldThreads,
89
+ statsByGroup,
90
+ ungroupedStats,
91
+ statusCounts,
92
+ groupMap,
93
+ };
94
+ }
95
+ //# sourceMappingURL=overview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overview.js","sourceRoot":"","sources":["../../src/domain/overview.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Progress entry creation and movement helpers.
3
+ *
4
+ * Pure functions — ID generation uses crypto.randomUUID() (Node 20+).
5
+ */
6
+ import { ProgressEntry } from '../models/types';
7
+ /**
8
+ * Create a new progress entry with generated ID and timestamp.
9
+ *
10
+ * @param note - Progress note text
11
+ * @param timestamp - Custom timestamp (defaults to now)
12
+ */
13
+ export declare function createProgressEntry(note: string, timestamp?: Date): ProgressEntry;
14
+ /**
15
+ * Move progress entries from source to destination.
16
+ * Returns updated arrays for both source and destination.
17
+ *
18
+ * @param sourceProgress - Source thread's progress entries
19
+ * @param destProgress - Destination thread's progress entries
20
+ * @param count - Number of most recent entries to move
21
+ * @returns Updated source and destination arrays, plus moved entries
22
+ */
23
+ export declare function moveProgressEntries(sourceProgress: ProgressEntry[], destProgress: ProgressEntry[], count: number): {
24
+ remaining: ProgressEntry[];
25
+ updated: ProgressEntry[];
26
+ moved: ProgressEntry[];
27
+ };
28
+ //# sourceMappingURL=progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/domain/progress.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,IAAiB,GAC3B,aAAa,CAMf;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,aAAa,EAAE,EAC/B,YAAY,EAAE,aAAa,EAAE,EAC7B,KAAK,EAAE,MAAM,GACZ;IACD,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB,CAmBA"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * Progress entry creation and movement helpers.
4
+ *
5
+ * Pure functions — ID generation uses crypto.randomUUID() (Node 20+).
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createProgressEntry = createProgressEntry;
9
+ exports.moveProgressEntries = moveProgressEntries;
10
+ /**
11
+ * Create a new progress entry with generated ID and timestamp.
12
+ *
13
+ * @param note - Progress note text
14
+ * @param timestamp - Custom timestamp (defaults to now)
15
+ */
16
+ function createProgressEntry(note, timestamp = new Date()) {
17
+ return {
18
+ id: crypto.randomUUID(),
19
+ timestamp: timestamp.toISOString(),
20
+ note,
21
+ };
22
+ }
23
+ /**
24
+ * Move progress entries from source to destination.
25
+ * Returns updated arrays for both source and destination.
26
+ *
27
+ * @param sourceProgress - Source thread's progress entries
28
+ * @param destProgress - Destination thread's progress entries
29
+ * @param count - Number of most recent entries to move
30
+ * @returns Updated source and destination arrays, plus moved entries
31
+ */
32
+ function moveProgressEntries(sourceProgress, destProgress, count) {
33
+ // Sort source by timestamp to get chronological order
34
+ const sorted = [...sourceProgress].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
35
+ const actualCount = Math.min(count, sorted.length);
36
+ if (actualCount === 0) {
37
+ return { remaining: sorted, updated: [...destProgress], moved: [] };
38
+ }
39
+ const moved = sorted.slice(-actualCount);
40
+ const remaining = sorted.slice(0, -actualCount);
41
+ // Merge moved entries into destination, sorted by timestamp
42
+ const updated = [...destProgress, ...moved].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
43
+ return { remaining, updated, moved };
44
+ }
45
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/domain/progress.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAUH,kDASC;AAWD,kDA2BC;AArDD;;;;;GAKG;AACH,SAAgB,mBAAmB,CACjC,IAAY,EACZ,YAAkB,IAAI,IAAI,EAAE;IAE5B,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;QACvB,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE;QAClC,IAAI;KACL,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,mBAAmB,CACjC,cAA+B,EAC/B,YAA6B,EAC7B,KAAa;IAMb,sDAAsD;IACtD,MAAM,MAAM,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC,IAAI,CACrC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAC5E,CAAC;IAEF,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,YAAY,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACtE,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC;IAEhD,4DAA4D;IAC5D,MAAM,OAAO,GAAG,CAAC,GAAG,YAAY,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAC9C,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAC5E,CAAC;IAEF,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACvC,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Thread scoring for next-action recommendations.
3
+ *
4
+ * Formula: (importance * 3) + (temperature * 2) + recency
5
+ * Pure functions — no storage dependencies.
6
+ */
7
+ import { Thread, Temperature } from '../models/types';
8
+ /** Temperature → numeric weight (0-5 scale). */
9
+ export declare const temperatureScores: Record<Temperature, number>;
10
+ /** A thread with its computed priority scores. */
11
+ export interface ScoredThread {
12
+ thread: Thread;
13
+ totalScore: number;
14
+ importanceScore: number;
15
+ temperatureScore: number;
16
+ recencyScore: number;
17
+ }
18
+ /**
19
+ * Compute a recency score that decays over time.
20
+ * Recent activity scores higher; activity >30 days old approaches 0.
21
+ *
22
+ * Formula: 5 * (1 / (1 + days/7))
23
+ * At 0 days: ~5, at 7 days: ~2.5, at 30 days: ~0.9
24
+ *
25
+ * @param isoDate - ISO timestamp of last activity
26
+ * @param now - Current time (injectable for testing)
27
+ */
28
+ export declare function recencyScore(isoDate: string, now?: Date): number;
29
+ /**
30
+ * Get the most recent activity date for a thread.
31
+ * Uses lastUpdated (derived from progress) with modified as fallback.
32
+ */
33
+ export declare function getMostRecentActivity(thread: Thread): string;
34
+ /**
35
+ * Calculate composite priority score for a thread.
36
+ *
37
+ * Weights: importance=3, temperature=2, recency=1
38
+ */
39
+ export declare function scoreThread(thread: Thread, now?: Date): ScoredThread;
40
+ //# sourceMappingURL=scoring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../../src/domain/scoring.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGtD,gDAAgD;AAChD,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAOzD,CAAC;AAEF,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAI5E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,YAAY,CAehF"}
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * Thread scoring for next-action recommendations.
4
+ *
5
+ * Formula: (importance * 3) + (temperature * 2) + recency
6
+ * Pure functions — no storage dependencies.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.temperatureScores = void 0;
10
+ exports.recencyScore = recencyScore;
11
+ exports.getMostRecentActivity = getMostRecentActivity;
12
+ exports.scoreThread = scoreThread;
13
+ const time_1 = require("./time");
14
+ /** Temperature → numeric weight (0-5 scale). */
15
+ exports.temperatureScores = {
16
+ frozen: 0,
17
+ freezing: 1,
18
+ cold: 2,
19
+ tepid: 3,
20
+ warm: 4,
21
+ hot: 5,
22
+ };
23
+ /**
24
+ * Compute a recency score that decays over time.
25
+ * Recent activity scores higher; activity >30 days old approaches 0.
26
+ *
27
+ * Formula: 5 * (1 / (1 + days/7))
28
+ * At 0 days: ~5, at 7 days: ~2.5, at 30 days: ~0.9
29
+ *
30
+ * @param isoDate - ISO timestamp of last activity
31
+ * @param now - Current time (injectable for testing)
32
+ */
33
+ function recencyScore(isoDate, now = new Date()) {
34
+ const hours = (0, time_1.hoursSince)(isoDate, now);
35
+ const days = hours / 24;
36
+ return 5 * (1 / (1 + days / 7));
37
+ }
38
+ /**
39
+ * Get the most recent activity date for a thread.
40
+ * Uses lastUpdated (derived from progress) with modified as fallback.
41
+ */
42
+ function getMostRecentActivity(thread) {
43
+ return thread.lastUpdated || thread.modified;
44
+ }
45
+ /**
46
+ * Calculate composite priority score for a thread.
47
+ *
48
+ * Weights: importance=3, temperature=2, recency=1
49
+ */
50
+ function scoreThread(thread, now = new Date()) {
51
+ const impScore = thread.importance;
52
+ const tempScore = exports.temperatureScores[thread.temperature ?? 'frozen'];
53
+ const recentActivity = getMostRecentActivity(thread);
54
+ const recScore = recencyScore(recentActivity, now);
55
+ const total = (impScore * 3) + (tempScore * 2) + recScore;
56
+ return {
57
+ thread,
58
+ totalScore: total,
59
+ importanceScore: impScore,
60
+ temperatureScore: tempScore,
61
+ recencyScore: recScore,
62
+ };
63
+ }
64
+ //# sourceMappingURL=scoring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../src/domain/scoring.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAkCH,oCAIC;AAMD,sDAEC;AAOD,kCAeC;AAjED,iCAAoC;AAEpC,gDAAgD;AACnC,QAAA,iBAAiB,GAAgC;IAC5D,MAAM,EAAE,CAAC;IACT,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,GAAG,EAAE,CAAC;CACP,CAAC;AAWF;;;;;;;;;GASG;AACH,SAAgB,YAAY,CAAC,OAAe,EAAE,MAAY,IAAI,IAAI,EAAE;IAClE,MAAM,KAAK,GAAG,IAAA,iBAAU,EAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;IACxB,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,SAAgB,qBAAqB,CAAC,MAAc;IAClD,OAAO,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,QAAQ,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,SAAgB,WAAW,CAAC,MAAc,EAAE,MAAY,IAAI,IAAI,EAAE;IAChE,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC;IACnC,MAAM,SAAS,GAAG,yBAAiB,CAAC,MAAM,CAAC,WAAW,IAAI,QAAQ,CAAC,CAAC;IACpE,MAAM,cAAc,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,YAAY,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IAEnD,MAAM,KAAK,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IAE1D,OAAO;QACL,MAAM;QACN,UAAU,EAAE,KAAK;QACjB,eAAe,EAAE,QAAQ;QACzB,gBAAgB,EAAE,SAAS;QAC3B,YAAY,EAAE,QAAQ;KACvB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Full-text search across thread fields.
3
+ *
4
+ * Pure functions — regex-based text matching with no storage dependencies.
5
+ */
6
+ import { Thread } from '../models/types';
7
+ /** Search scope: which thread fields to search. */
8
+ export type SearchScope = 'name' | 'progress' | 'details' | 'tags' | 'all';
9
+ /** A single match position within text. */
10
+ export interface MatchContext {
11
+ scope: SearchScope;
12
+ text: string;
13
+ matchStart: number;
14
+ matchEnd: number;
15
+ }
16
+ /** A thread with its match contexts. */
17
+ export interface SearchMatch {
18
+ thread: Thread;
19
+ matches: MatchContext[];
20
+ }
21
+ /**
22
+ * Find all occurrences of a pattern in text, returning match contexts.
23
+ *
24
+ * Uses substring indexOf (not regex) for simplicity and safety.
25
+ */
26
+ export declare function findMatches(text: string, pattern: string, scope: SearchScope, caseSensitive: boolean): MatchContext[];
27
+ /**
28
+ * Search a single thread for matches across the specified scope(s).
29
+ */
30
+ export declare function searchThread(thread: Thread, pattern: string, scope: SearchScope, caseSensitive: boolean): MatchContext[];
31
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/domain/search.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3E,2CAA2C;AAC3C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wCAAwC;AACxC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,OAAO,GACrB,YAAY,EAAE,CAoBhB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,OAAO,GACrB,YAAY,EAAE,CA0BhB"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /**
3
+ * Full-text search across thread fields.
4
+ *
5
+ * Pure functions — regex-based text matching with no storage dependencies.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.findMatches = findMatches;
9
+ exports.searchThread = searchThread;
10
+ /**
11
+ * Find all occurrences of a pattern in text, returning match contexts.
12
+ *
13
+ * Uses substring indexOf (not regex) for simplicity and safety.
14
+ */
15
+ function findMatches(text, pattern, scope, caseSensitive) {
16
+ const results = [];
17
+ const searchText = caseSensitive ? text : text.toLowerCase();
18
+ const searchPattern = caseSensitive ? pattern : pattern.toLowerCase();
19
+ let pos = 0;
20
+ while (pos < searchText.length) {
21
+ const idx = searchText.indexOf(searchPattern, pos);
22
+ if (idx === -1)
23
+ break;
24
+ results.push({
25
+ scope,
26
+ text,
27
+ matchStart: idx,
28
+ matchEnd: idx + pattern.length,
29
+ });
30
+ pos = idx + 1;
31
+ }
32
+ return results;
33
+ }
34
+ /**
35
+ * Search a single thread for matches across the specified scope(s).
36
+ */
37
+ function searchThread(thread, pattern, scope, caseSensitive) {
38
+ const matches = [];
39
+ if (scope === 'all' || scope === 'name') {
40
+ matches.push(...findMatches(thread.name, pattern, 'name', caseSensitive));
41
+ }
42
+ if (scope === 'all' || scope === 'progress') {
43
+ for (const entry of thread.progress || []) {
44
+ matches.push(...findMatches(entry.note, pattern, 'progress', caseSensitive));
45
+ }
46
+ }
47
+ if (scope === 'all' || scope === 'details') {
48
+ for (const entry of thread.details || []) {
49
+ matches.push(...findMatches(entry.content, pattern, 'details', caseSensitive));
50
+ }
51
+ }
52
+ if (scope === 'all' || scope === 'tags') {
53
+ for (const tag of thread.tags || []) {
54
+ matches.push(...findMatches(tag, pattern, 'tags', caseSensitive));
55
+ }
56
+ }
57
+ return matches;
58
+ }
59
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/domain/search.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AA0BH,kCAyBC;AAKD,oCA+BC;AAlED;;;;GAIG;AACH,SAAgB,WAAW,CACzB,IAAY,EACZ,OAAe,EACf,KAAkB,EAClB,aAAsB;IAEtB,MAAM,OAAO,GAAmB,EAAE,CAAC;IACnC,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IAC7D,MAAM,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IAEtE,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,OAAO,GAAG,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QACnD,IAAI,GAAG,KAAK,CAAC,CAAC;YAAE,MAAM;QAEtB,OAAO,CAAC,IAAI,CAAC;YACX,KAAK;YACL,IAAI;YACJ,UAAU,EAAE,GAAG;YACf,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM;SAC/B,CAAC,CAAC;QACH,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;IAChB,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAgB,YAAY,CAC1B,MAAc,EACd,OAAe,EACf,KAAkB,EAClB,aAAsB;IAEtB,MAAM,OAAO,GAAmB,EAAE,CAAC;IAEnC,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;QAC5C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;YAC1C,OAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC3C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;YACzC,OAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAED,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Thread sorting functions.
3
+ *
4
+ * Pure functions — no storage dependencies.
5
+ * Uses TEMPERATURE_ORDER from models for canonical ordering.
6
+ */
7
+ import { Thread, Temperature } from '../models/types';
8
+ /** Temperature values considered "cold" for attention logic. */
9
+ export declare const COLD_TEMPS: Temperature[];
10
+ /**
11
+ * Sort threads by priority: temperature (hottest first), importance (highest first),
12
+ * then lastUpdated (most recent first).
13
+ *
14
+ * Returns a new array — does not mutate the input.
15
+ */
16
+ export declare function sortByPriority(threads: Thread[]): Thread[];
17
+ //# sourceMappingURL=sorting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sorting.d.ts","sourceRoot":"","sources":["../../src/domain/sorting.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGtD,gEAAgE;AAChE,eAAO,MAAM,UAAU,EAAE,WAAW,EAAmC,CAAC;AAExE;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAS1D"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * Thread sorting functions.
4
+ *
5
+ * Pure functions — no storage dependencies.
6
+ * Uses TEMPERATURE_ORDER from models for canonical ordering.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.COLD_TEMPS = void 0;
10
+ exports.sortByPriority = sortByPriority;
11
+ const temperature_1 = require("../models/temperature");
12
+ /** Temperature values considered "cold" for attention logic. */
13
+ exports.COLD_TEMPS = ['cold', 'freezing', 'frozen'];
14
+ /**
15
+ * Sort threads by priority: temperature (hottest first), importance (highest first),
16
+ * then lastUpdated (most recent first).
17
+ *
18
+ * Returns a new array — does not mutate the input.
19
+ */
20
+ function sortByPriority(threads) {
21
+ return [...threads].sort((a, b) => {
22
+ const tempDiff = temperature_1.TEMPERATURE_ORDER.indexOf(a.temperature ?? 'frozen') - temperature_1.TEMPERATURE_ORDER.indexOf(b.temperature ?? 'frozen');
23
+ if (tempDiff !== 0)
24
+ return tempDiff;
25
+ if (a.importance !== b.importance)
26
+ return b.importance - a.importance;
27
+ const aTime = new Date(a.lastUpdated || a.modified).getTime();
28
+ const bTime = new Date(b.lastUpdated || b.modified).getTime();
29
+ return bTime - aTime;
30
+ });
31
+ }
32
+ //# sourceMappingURL=sorting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sorting.js","sourceRoot":"","sources":["../../src/domain/sorting.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAcH,wCASC;AApBD,uDAA0D;AAE1D,gEAAgE;AACnD,QAAA,UAAU,GAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;AAExE;;;;;GAKG;AACH,SAAgB,cAAc,CAAC,OAAiB;IAC9C,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAChC,MAAM,QAAQ,GAAG,+BAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,IAAI,QAAQ,CAAC,GAAG,+BAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,IAAI,QAAQ,CAAC,CAAC;QAC7H,IAAI,QAAQ,KAAK,CAAC;YAAE,OAAO,QAAQ,CAAC;QACpC,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU;YAAE,OAAO,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;QACtE,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;QAC9D,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;QAC9D,OAAO,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Date/time utility functions for thread domain logic.
3
+ *
4
+ * Pure functions — no storage or side-effect dependencies.
5
+ * Deduplicates copies from agenda.ts, overview.ts, next.ts.
6
+ */
7
+ import { Thread } from '../models/types';
8
+ /**
9
+ * Calculate calendar days since a given ISO date string.
10
+ *
11
+ * @param isoDate - ISO 8601 timestamp
12
+ * @param now - Current time (injectable for testing)
13
+ * @returns Integer number of days elapsed
14
+ */
15
+ export declare function daysSince(isoDate: string, now?: Date): number;
16
+ /**
17
+ * Calculate hours since a given ISO date string.
18
+ *
19
+ * @param isoDate - ISO 8601 timestamp
20
+ * @param now - Current time (injectable for testing)
21
+ * @returns Fractional hours elapsed
22
+ */
23
+ export declare function hoursSince(isoDate: string, now?: Date): number;
24
+ /**
25
+ * Check if a date is today.
26
+ *
27
+ * @param isoDate - ISO 8601 timestamp
28
+ * @param now - Current time (injectable for testing)
29
+ */
30
+ export declare function isToday(isoDate: string, now?: Date): boolean;
31
+ /**
32
+ * Check if a date is within the last N days (inclusive).
33
+ *
34
+ * @param isoDate - ISO 8601 timestamp
35
+ * @param days - Number of days to look back
36
+ * @param now - Current time (injectable for testing)
37
+ */
38
+ export declare function isWithinDays(isoDate: string, days: number, now?: Date): boolean;
39
+ /**
40
+ * Format a relative time string for display.
41
+ *
42
+ * @returns "today", "yesterday", "3d ago", "2w ago", "1mo ago"
43
+ */
44
+ export declare function formatRelativeTime(isoDate: string, now?: Date): string;
45
+ /**
46
+ * Get the most recent progress entry timestamp for a thread.
47
+ *
48
+ * @returns ISO timestamp string or null if no progress
49
+ */
50
+ export declare function getLastProgressDate(thread: Thread): string | null;
51
+ /**
52
+ * Get the most recent progress note text for a thread.
53
+ *
54
+ * @returns Note string or null if no progress
55
+ */
56
+ export declare function getLastProgressNote(thread: Thread): string | null;
57
+ /**
58
+ * Truncate a string to a max length with ellipsis.
59
+ *
60
+ * @param str - Input string
61
+ * @param maxLen - Maximum output length (including ellipsis)
62
+ * @returns Truncated string
63
+ */
64
+ export declare function truncate(str: string, maxLen: number): string;
65
+ /**
66
+ * Parse a date string loosely. Supports ISO dates and natural language.
67
+ *
68
+ * @returns Date object or null if unparseable
69
+ */
70
+ export declare function parseDate(dateStr: string): Date | null;
71
+ /**
72
+ * Parse a datetime string with natural language support.
73
+ * Supports: ISO dates, "yesterday", "N days ago".
74
+ *
75
+ * @returns Date object or null if unparseable
76
+ */
77
+ export declare function parseDateTime(input: string, now?: Date): Date | null;
78
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/domain/time.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAIzE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAI1E;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,OAAO,CAGxE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,OAAO,CAE3F;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAOlF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGjE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGjE;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGtD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,IAAI,GAAG,IAAI,CAkBhF"}