botnote 0.1.24 → 0.1.26

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.
@@ -1,28 +1,29 @@
1
1
  import { and, desc, eq, gte, inArray, isNull, sql } from "drizzle-orm";
2
2
  import { edges, entities, projects } from "../db/schema.js";
3
- /**
4
- * Snap an exact-midnight-UTC due date to noon UTC. Date-only intents (e.g.
5
- * "due 2026-06-03") arriving as `2026-06-03T00:00:00Z` would render as the
6
- * previous calendar day in any UTC-negative timezone. Noon UTC stays on the
7
- * intended day across UTC-12..UTC+11. Anything with a non-zero time
8
- * component is left alone — that's a real datetime, not a calendar date.
9
- */
10
- function normalizeDueAt(value) {
11
- if (value == null)
12
- return null;
13
- if (value.getUTCHours() === 0 &&
14
- value.getUTCMinutes() === 0 &&
15
- value.getUTCSeconds() === 0 &&
16
- value.getUTCMilliseconds() === 0) {
17
- const noon = new Date(value);
18
- noon.setUTCHours(12);
19
- return noon;
20
- }
21
- return value;
22
- }
3
+ import { normalizeDueAt } from "./dates.js";
4
+ import { advanceRecurrenceOnCompletion } from "./recurrence.js";
5
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6
+ const UUID_PREFIX_RE = /^[0-9a-f-]{8,36}$/i;
23
7
  function normalizeStatus(value) {
24
8
  return value === "delayed" || value === "archived" ? "done" : value;
25
9
  }
10
+ export async function resolveEntityId(db, idOrPrefix) {
11
+ if (UUID_RE.test(idOrPrefix))
12
+ return idOrPrefix;
13
+ if (!UUID_PREFIX_RE.test(idOrPrefix))
14
+ return null;
15
+ const rows = (await db.execute(sql `
16
+ SELECT id::text AS id
17
+ FROM entities
18
+ WHERE id::text LIKE ${`${idOrPrefix.toLowerCase()}%`}
19
+ LIMIT 2
20
+ `)).rows;
21
+ if (rows.length === 1)
22
+ return rows[0].id;
23
+ if (rows.length > 1)
24
+ throw new Error(`entity id prefix ${idOrPrefix} is ambiguous`);
25
+ return null;
26
+ }
26
27
  export async function write(db, input) {
27
28
  if (input.idempotencyKey) {
28
29
  const existing = await db
@@ -66,6 +67,9 @@ export async function write(db, input) {
66
67
  return row;
67
68
  }
68
69
  export async function update(db, id, fields) {
70
+ const entityId = await resolveEntityId(db, id);
71
+ if (!entityId)
72
+ throw new Error(`entity ${id} not found`);
69
73
  const normalizedFields = { ...fields };
70
74
  if (fields.status !== undefined) {
71
75
  normalizedFields.status = normalizeStatus(fields.status);
@@ -78,22 +82,24 @@ export async function update(db, id, fields) {
78
82
  // prior status to know whether this is an entry into or exit from 'done',
79
83
  // so do a tiny read first — cheaper than a CASE expression on UPDATE and
80
84
  // keeps the SQL the same shape as other writes.
85
+ let enteredDone = false;
81
86
  if (normalizedFields.status !== undefined) {
82
87
  const prior = await db
83
88
  .select({ status: entities.status })
84
89
  .from(entities)
85
- .where(eq(entities.id, id))
90
+ .where(eq(entities.id, entityId))
86
91
  .limit(1);
87
92
  if (!prior[0])
88
93
  throw new Error(`entity ${id} not found`);
89
94
  const wasDone = prior[0].status === "done";
90
95
  const isDone = normalizedFields.status === "done";
96
+ enteredDone = isDone && !wasDone;
91
97
  if (isDone && !wasDone)
92
98
  set.completedAt = new Date();
93
99
  else if (!isDone && wasDone)
94
100
  set.completedAt = null;
95
101
  }
96
- const [row] = await db.update(entities).set(set).where(eq(entities.id, id)).returning();
102
+ const [row] = await db.update(entities).set(set).where(eq(entities.id, entityId)).returning();
97
103
  if (!row)
98
104
  throw new Error(`entity ${id} not found`);
99
105
  if (fields.parentId !== undefined && fields.parentId !== null) {
@@ -102,17 +108,26 @@ export async function update(db, id, fields) {
102
108
  .values({ fromId: fields.parentId, toId: row.id, kind: "parent_of" })
103
109
  .onConflictDoNothing();
104
110
  }
111
+ if (enteredDone && row.kind === "task") {
112
+ await advanceRecurrenceOnCompletion(db, row);
113
+ }
105
114
  return row;
106
115
  }
107
116
  export async function listRelated(db, parentId) {
117
+ const entityId = await resolveEntityId(db, parentId);
118
+ if (!entityId)
119
+ return [];
108
120
  return db
109
121
  .select()
110
122
  .from(entities)
111
- .where(eq(entities.parentId, parentId))
123
+ .where(eq(entities.parentId, entityId))
112
124
  .orderBy(desc(entities.createdAt));
113
125
  }
114
126
  export async function get(db, id) {
115
- const rows = await db.select().from(entities).where(eq(entities.id, id)).limit(1);
127
+ const entityId = await resolveEntityId(db, id);
128
+ if (!entityId)
129
+ return null;
130
+ const rows = await db.select().from(entities).where(eq(entities.id, entityId)).limit(1);
116
131
  return rows[0] ?? null;
117
132
  }
118
133
  /**
@@ -129,7 +144,13 @@ export async function getByKey(db, projectKey, sequenceId) {
129
144
  return rows[0]?.e ?? null;
130
145
  }
131
146
  export async function remove(db, id) {
132
- const res = await db.delete(entities).where(eq(entities.id, id)).returning({ id: entities.id });
147
+ const entityId = await resolveEntityId(db, id);
148
+ if (!entityId)
149
+ return false;
150
+ const res = await db
151
+ .delete(entities)
152
+ .where(eq(entities.id, entityId))
153
+ .returning({ id: entities.id });
133
154
  return res.length > 0;
134
155
  }
135
156
  export async function recent(db, input) {
@@ -1 +1 @@
1
- {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../src/service/entities.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../src/service/entities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { Database } from "../db/client.js";
2
+ import { type Entity, type RecurrenceRule } from "../db/schema.js";
3
+ import type { RecurrenceInput, StopRecurrenceInput, UpdateRecurrenceInput } from "./types.js";
4
+ export interface RecurrenceDetails {
5
+ rule: RecurrenceRule;
6
+ currentOccurrence: Entity | null;
7
+ }
8
+ export declare function getRecurrenceForTask(db: Database["db"], taskId: string): Promise<RecurrenceDetails | null>;
9
+ export declare function createRecurrenceRule(db: Database["db"], taskId: string, input: RecurrenceInput): Promise<RecurrenceRule>;
10
+ export declare function updateRecurrenceRule(db: Database["db"], ruleId: string, input: UpdateRecurrenceInput): Promise<RecurrenceRule>;
11
+ export declare function advanceRecurrenceOnCompletion(db: Database["db"], occurrence: Entity, completedAt?: Date): Promise<Entity | null>;
12
+ export declare function skipOccurrence(db: Database["db"], taskId: string, input?: {
13
+ reason?: string;
14
+ actorKind?: string;
15
+ }): Promise<{
16
+ skipped: Entity;
17
+ next: Entity | null;
18
+ rule: RecurrenceRule;
19
+ }>;
20
+ export declare function stopRecurrence(db: Database["db"], ruleId: string, _input?: StopRecurrenceInput): Promise<RecurrenceRule>;
@@ -0,0 +1,381 @@
1
+ import { and, eq, or } from "drizzle-orm";
2
+ import rrulePkg from "rrule";
3
+ import { entities, recurrenceExceptions, recurrenceRules } from "../db/schema.js";
4
+ import { normalizeDueAt } from "./dates.js";
5
+ const { RRule } = rrulePkg;
6
+ const WEEKDAYS = {
7
+ MO: RRule.MO,
8
+ TU: RRule.TU,
9
+ WE: RRule.WE,
10
+ TH: RRule.TH,
11
+ FR: RRule.FR,
12
+ SA: RRule.SA,
13
+ SU: RRule.SU
14
+ };
15
+ const FREQUENCIES = {
16
+ hourly: RRule.HOURLY,
17
+ daily: RRule.DAILY,
18
+ weekly: RRule.WEEKLY,
19
+ monthly: RRule.MONTHLY,
20
+ yearly: RRule.YEARLY
21
+ };
22
+ function metadataObject(value) {
23
+ if (!value || typeof value !== "object" || Array.isArray(value))
24
+ return {};
25
+ return { ...value };
26
+ }
27
+ function recurrenceMarker(e) {
28
+ const metadata = metadataObject(e.metadata);
29
+ if (!metadata.recurrence || typeof metadata.recurrence !== "object")
30
+ return null;
31
+ return metadata.recurrence;
32
+ }
33
+ function withRecurrenceMetadata(e, marker) {
34
+ return {
35
+ ...metadataObject(e.metadata),
36
+ recurrence: marker
37
+ };
38
+ }
39
+ function normalizeRRuleText(raw) {
40
+ const lines = raw
41
+ .split(/\r?\n/)
42
+ .map((line) => line.trim())
43
+ .filter(Boolean);
44
+ const rruleLine = lines.find((line) => line.toUpperCase().startsWith("RRULE:")) ?? lines[0];
45
+ if (!rruleLine)
46
+ throw new Error("recurrence rule is empty");
47
+ return rruleLine.replace(/^RRULE:/i, "");
48
+ }
49
+ function rruleLine(rule) {
50
+ const line = rule
51
+ .toString()
52
+ .split(/\r?\n/)
53
+ .find((part) => part.startsWith("RRULE:"));
54
+ if (!line)
55
+ throw new Error("rrule did not produce an RRULE line");
56
+ return line.slice("RRULE:".length);
57
+ }
58
+ function buildRRuleFromInput(input, dtstart) {
59
+ if (input.rrule)
60
+ return normalizeRRuleText(input.rrule);
61
+ if (!input.preset)
62
+ throw new Error("recurrence preset is required");
63
+ const options = {
64
+ freq: FREQUENCIES[input.preset],
65
+ interval: input.interval,
66
+ dtstart
67
+ };
68
+ if (input.byWeekday?.length) {
69
+ options.byweekday = input.byWeekday.map((day) => WEEKDAYS[day]);
70
+ }
71
+ if (input.byMonthDay?.length)
72
+ options.bymonthday = input.byMonthDay;
73
+ if (input.bySetPos != null)
74
+ options.bysetpos = input.bySetPos;
75
+ if (input.byMonth?.length)
76
+ options.bymonth = input.byMonth;
77
+ if (input.until)
78
+ options.until = input.until;
79
+ if (input.count)
80
+ options.count = input.count;
81
+ return rruleLine(new RRule(options));
82
+ }
83
+ function buildRRule(rule, dtstart = rule.dtstart) {
84
+ const parsed = RRule.parseString(rule.rrule);
85
+ return new RRule({ ...parsed, dtstart });
86
+ }
87
+ function maxDate(a, b) {
88
+ return a.getTime() >= b.getTime() ? a : b;
89
+ }
90
+ function countLimit(rule) {
91
+ return buildRRule(rule).options.count ?? null;
92
+ }
93
+ function computeNextOccurrenceAt(rule, occurrence, completedAt) {
94
+ const count = countLimit(rule);
95
+ if (count != null && rule.generatedCount >= count)
96
+ return null;
97
+ const anchor = rule.anchor === "completion" ? "completion" : "scheduled";
98
+ const dtstart = anchor === "completion" ? completedAt : rule.dtstart;
99
+ const rrule = buildRRule(rule, dtstart);
100
+ const scheduledBase = occurrence.dueAt ?? rule.dtstart;
101
+ const cursor = anchor === "completion" ? completedAt : maxDate(scheduledBase, completedAt);
102
+ const next = rrule.after(cursor, false);
103
+ return next ? normalizeDueAt(next) : null;
104
+ }
105
+ async function fetchEntity(db, id) {
106
+ const rows = await db.select().from(entities).where(eq(entities.id, id)).limit(1);
107
+ const row = rows[0];
108
+ if (!row)
109
+ throw new Error(`entity ${id} not found`);
110
+ return row;
111
+ }
112
+ async function findRuleForTask(db, task, includeDisabled = true) {
113
+ const marker = recurrenceMarker(task);
114
+ const cond = marker?.ruleId
115
+ ? eq(recurrenceRules.id, marker.ruleId)
116
+ : or(eq(recurrenceRules.currentOccurrenceId, task.id), eq(recurrenceRules.seriesId, task.id));
117
+ const where = includeDisabled ? cond : and(cond, eq(recurrenceRules.enabled, true));
118
+ const rows = await db.select().from(recurrenceRules).where(where).limit(1);
119
+ return rows[0] ?? null;
120
+ }
121
+ export async function getRecurrenceForTask(db, taskId) {
122
+ const task = await fetchEntity(db, taskId);
123
+ const rule = await findRuleForTask(db, task);
124
+ if (!rule)
125
+ return null;
126
+ const currentOccurrence = rule.currentOccurrenceId
127
+ ? await fetchEntity(db, rule.currentOccurrenceId).catch(() => null)
128
+ : null;
129
+ return { rule, currentOccurrence };
130
+ }
131
+ export async function createRecurrenceRule(db, taskId, input) {
132
+ const task = await fetchEntity(db, taskId);
133
+ if (task.kind !== "task")
134
+ throw new Error("recurrence can only be attached to tasks");
135
+ if (!task.dueAt)
136
+ throw new Error("recurring tasks require a first due date");
137
+ if (task.status === "done" || task.status === "rejected") {
138
+ throw new Error("recurrence cannot be attached to a terminal task");
139
+ }
140
+ const existingRule = await findRuleForTask(db, task);
141
+ const seriesId = existingRule?.seriesId ?? recurrenceMarker(task)?.seriesId ?? task.id;
142
+ const dtstart = input.dtstart ? normalizeDueAt(input.dtstart) : task.dueAt;
143
+ const rrule = buildRRuleFromInput(input, dtstart);
144
+ const nextOccurrenceAt = buildRRule({ rrule, dtstart }).after(dtstart, false);
145
+ let rule;
146
+ if (existingRule) {
147
+ const [updated] = await db
148
+ .update(recurrenceRules)
149
+ .set({
150
+ currentOccurrenceId: task.id,
151
+ enabled: true,
152
+ rrule,
153
+ dtstart,
154
+ timezone: input.timezone,
155
+ allDay: input.allDay,
156
+ anchor: input.anchor,
157
+ maxInstancesAhead: 1,
158
+ nextOccurrenceAt: nextOccurrenceAt ? normalizeDueAt(nextOccurrenceAt) : null,
159
+ endedAt: null
160
+ })
161
+ .where(eq(recurrenceRules.id, existingRule.id))
162
+ .returning();
163
+ if (!updated)
164
+ throw new Error(`recurrence rule ${existingRule.id} not found`);
165
+ rule = updated;
166
+ }
167
+ else {
168
+ const [inserted] = await db
169
+ .insert(recurrenceRules)
170
+ .values({
171
+ seriesId,
172
+ currentOccurrenceId: task.id,
173
+ enabled: true,
174
+ rrule,
175
+ dtstart,
176
+ timezone: input.timezone,
177
+ allDay: input.allDay,
178
+ anchor: input.anchor,
179
+ maxInstancesAhead: 1,
180
+ generatedCount: 1,
181
+ lastOccurrenceAt: null,
182
+ nextOccurrenceAt: nextOccurrenceAt ? normalizeDueAt(nextOccurrenceAt) : null,
183
+ endedAt: null
184
+ })
185
+ .returning();
186
+ if (!inserted)
187
+ throw new Error("recurrence rule insert returned no row");
188
+ rule = inserted;
189
+ }
190
+ await db
191
+ .update(entities)
192
+ .set({
193
+ metadata: withRecurrenceMetadata(task, {
194
+ ruleId: rule.id,
195
+ seriesId: rule.seriesId,
196
+ role: "occurrence",
197
+ occurrenceAt: (task.dueAt ?? rule.dtstart).toISOString(),
198
+ occurrenceIndex: 1
199
+ })
200
+ })
201
+ .where(eq(entities.id, task.id));
202
+ return rule;
203
+ }
204
+ export async function updateRecurrenceRule(db, ruleId, input) {
205
+ const rows = await db.select().from(recurrenceRules).where(eq(recurrenceRules.id, ruleId)).limit(1);
206
+ const current = rows[0];
207
+ if (!current)
208
+ throw new Error(`recurrence rule ${ruleId} not found`);
209
+ const currentOccurrence = current.currentOccurrenceId
210
+ ? await fetchEntity(db, current.currentOccurrenceId)
211
+ : null;
212
+ const dtstart = input.dtstart
213
+ ? normalizeDueAt(input.dtstart)
214
+ : currentOccurrence?.dueAt ?? current.dtstart;
215
+ const nextRRuleInput = {
216
+ rrule: input.rrule ?? current.rrule,
217
+ preset: input.preset,
218
+ interval: input.interval ?? 1,
219
+ byWeekday: input.byWeekday,
220
+ byMonthDay: input.byMonthDay,
221
+ bySetPos: input.bySetPos,
222
+ byMonth: input.byMonth,
223
+ until: input.until,
224
+ count: input.count,
225
+ dtstart,
226
+ timezone: input.timezone ?? current.timezone,
227
+ allDay: input.allDay ?? current.allDay,
228
+ anchor: input.anchor ?? (current.anchor === "completion" ? "completion" : "scheduled")
229
+ };
230
+ const rrule = input.rrule || input.preset ? buildRRuleFromInput(nextRRuleInput, dtstart) : current.rrule;
231
+ const nextOccurrenceAt = buildRRule({ rrule, dtstart }).after(dtstart, false);
232
+ const [updated] = await db
233
+ .update(recurrenceRules)
234
+ .set({
235
+ enabled: input.enabled ?? current.enabled,
236
+ rrule,
237
+ dtstart,
238
+ timezone: input.timezone ?? current.timezone,
239
+ allDay: input.allDay ?? current.allDay,
240
+ anchor: input.anchor ?? current.anchor,
241
+ nextOccurrenceAt: nextOccurrenceAt ? normalizeDueAt(nextOccurrenceAt) : null,
242
+ endedAt: input.enabled === true ? null : current.endedAt
243
+ })
244
+ .where(eq(recurrenceRules.id, ruleId))
245
+ .returning();
246
+ if (!updated)
247
+ throw new Error(`recurrence rule ${ruleId} not found`);
248
+ return updated;
249
+ }
250
+ async function insertNextOccurrence(db, rule, occurrence, nextDueAt) {
251
+ const occurrenceIndex = rule.generatedCount + 1;
252
+ const idempotencyKey = `recurrence:${rule.id}:${nextDueAt.toISOString()}`;
253
+ const metadata = {
254
+ ...metadataObject(occurrence.metadata),
255
+ recurrence: {
256
+ ruleId: rule.id,
257
+ seriesId: rule.seriesId,
258
+ role: "occurrence",
259
+ occurrenceAt: nextDueAt.toISOString(),
260
+ occurrenceIndex
261
+ }
262
+ };
263
+ const values = {
264
+ kind: "task",
265
+ projectId: occurrence.projectId,
266
+ title: occurrence.title,
267
+ body: occurrence.body,
268
+ tags: occurrence.tags,
269
+ status: "open",
270
+ parentId: occurrence.parentId,
271
+ actorKind: "system",
272
+ metadata,
273
+ dueAt: nextDueAt,
274
+ priority: occurrence.priority,
275
+ pinned: false,
276
+ completedAt: null,
277
+ idempotencyKey
278
+ };
279
+ const inserted = await db
280
+ .insert(entities)
281
+ .values(values)
282
+ .onConflictDoNothing()
283
+ .returning();
284
+ if (inserted[0])
285
+ return inserted[0];
286
+ const rows = await db.select().from(entities).where(eq(entities.idempotencyKey, idempotencyKey));
287
+ const existing = rows[0];
288
+ if (!existing)
289
+ throw new Error("recurrence idempotent insert returned no row");
290
+ return existing;
291
+ }
292
+ async function advanceRuleFromOccurrence(db, rule, occurrence, completedAt) {
293
+ if (!rule.enabled)
294
+ return null;
295
+ const nextDueAt = computeNextOccurrenceAt(rule, occurrence, completedAt);
296
+ if (!nextDueAt) {
297
+ await db
298
+ .update(recurrenceRules)
299
+ .set({
300
+ enabled: false,
301
+ currentOccurrenceId: null,
302
+ lastOccurrenceAt: occurrence.dueAt ?? rule.dtstart,
303
+ nextOccurrenceAt: null,
304
+ endedAt: completedAt
305
+ })
306
+ .where(eq(recurrenceRules.id, rule.id));
307
+ return null;
308
+ }
309
+ const next = await insertNextOccurrence(db, rule, occurrence, nextDueAt);
310
+ await db
311
+ .update(recurrenceRules)
312
+ .set({
313
+ currentOccurrenceId: next.id,
314
+ generatedCount: Math.max(rule.generatedCount + 1, rule.generatedCount),
315
+ lastOccurrenceAt: occurrence.dueAt ?? rule.dtstart,
316
+ nextOccurrenceAt: nextDueAt
317
+ })
318
+ .where(eq(recurrenceRules.id, rule.id));
319
+ return next;
320
+ }
321
+ export async function advanceRecurrenceOnCompletion(db, occurrence, completedAt = occurrence.completedAt ?? new Date()) {
322
+ if (occurrence.kind !== "task")
323
+ return null;
324
+ const rule = await findRuleForTask(db, occurrence, false);
325
+ if (!rule)
326
+ return null;
327
+ if (rule.currentOccurrenceId && rule.currentOccurrenceId !== occurrence.id)
328
+ return null;
329
+ return advanceRuleFromOccurrence(db, rule, occurrence, completedAt);
330
+ }
331
+ export async function skipOccurrence(db, taskId, input = {}) {
332
+ const task = await fetchEntity(db, taskId);
333
+ if (task.kind !== "task")
334
+ throw new Error("only tasks can be skipped");
335
+ const rule = await findRuleForTask(db, task, false);
336
+ if (!rule)
337
+ throw new Error("task is not an active recurring occurrence");
338
+ if (rule.currentOccurrenceId && rule.currentOccurrenceId !== task.id) {
339
+ throw new Error("only the current recurring occurrence can be skipped");
340
+ }
341
+ await db.insert(recurrenceExceptions).values({
342
+ ruleId: rule.id,
343
+ occurrenceAt: task.dueAt ?? rule.dtstart,
344
+ action: "skipped",
345
+ entityId: task.id,
346
+ metadata: { reason: input.reason ?? null, actorKind: input.actorKind ?? "human" }
347
+ });
348
+ const [skipped] = await db
349
+ .update(entities)
350
+ .set({
351
+ status: "rejected",
352
+ updatedAt: new Date(),
353
+ metadata: {
354
+ ...metadataObject(task.metadata),
355
+ recurrence: recurrenceMarker(task),
356
+ skippedAt: new Date().toISOString(),
357
+ skipReason: input.reason ?? null
358
+ }
359
+ })
360
+ .where(eq(entities.id, task.id))
361
+ .returning();
362
+ if (!skipped)
363
+ throw new Error(`entity ${task.id} not found`);
364
+ const next = await advanceRuleFromOccurrence(db, rule, skipped, new Date());
365
+ return { skipped, next, rule };
366
+ }
367
+ export async function stopRecurrence(db, ruleId, _input = {}) {
368
+ const [updated] = await db
369
+ .update(recurrenceRules)
370
+ .set({
371
+ enabled: false,
372
+ nextOccurrenceAt: null,
373
+ endedAt: new Date()
374
+ })
375
+ .where(eq(recurrenceRules.id, ruleId))
376
+ .returning();
377
+ if (!updated)
378
+ throw new Error(`recurrence rule ${ruleId} not found`);
379
+ return updated;
380
+ }
381
+ //# sourceMappingURL=recurrence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recurrence.js","sourceRoot":"","sources":["../../src/service/recurrence.ts"],"names":[],"mappings":"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"}