calendaryjs 0.2.3 → 0.3.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.
@@ -228,8 +228,10 @@ interface ConstEvent<TMetadata extends Record<string, unknown> = Record<string,
228
228
  */
229
229
  month: number;
230
230
  /**
231
- * Day of the month (1-31)
231
+ * Day of the month. `1`–`31`, or **negative to count from the end**
232
+ * (`-1` = last day, `-2` = second-to-last).
232
233
  * @example 25 // 25th day
234
+ * @example -1 // last day of the month
233
235
  */
234
236
  day: number;
235
237
  /**
@@ -292,9 +294,12 @@ interface FixedEvent<TMetadata extends Record<string, unknown> = Record<string,
292
294
  interface MonthlyEvent<TMetadata extends Record<string, unknown> = Record<string, unknown>, TCategory extends string = string> extends BaseEventProperties<TMetadata, TCategory> {
293
295
  type: "monthly";
294
296
  /**
295
- * Day of the month (1-31)
296
- * Note: If day exceeds month's days (e.g., 31 in February), event is skipped for that month
297
+ * Day of the month. `1`–`31`, or **negative to count from the end**
298
+ * (`-1` = last day of every month, `-2` = second-to-last).
299
+ * Note: a positive day past the month's length (e.g. 31 in February) is
300
+ * skipped for that month.
297
301
  * @example 15 // 15th of each month
302
+ * @example -1 // last day of every month
298
303
  */
299
304
  day: number;
300
305
  /**
@@ -372,6 +377,43 @@ interface WeeklyEvent<TMetadata extends Record<string, unknown> = Record<string,
372
377
  */
373
378
  excludeDates?: string[];
374
379
  }
380
+ /**
381
+ * DAILY - Recurring events every day, or every N days.
382
+ * Supports an interval, a date range, and date exclusions. With `interval > 1`
383
+ * the cadence is phased off `startDate` (or a fixed epoch when omitted) so
384
+ * "every N days" stays in step across the year boundary instead of resetting.
385
+ * @template TMetadata - Custom metadata type extending Record<string, unknown>
386
+ * @example
387
+ * // Every day
388
+ * { type: "daily", id: "standup" }
389
+ *
390
+ * // Every 3 days, anchored to a start date
391
+ * { type: "daily", id: "meds", interval: 3, startDate: "2025-01-01" }
392
+ */
393
+ interface DailyEvent<TMetadata extends Record<string, unknown> = Record<string, unknown>, TCategory extends string = string> extends BaseEventProperties<TMetadata, TCategory> {
394
+ type: "daily";
395
+ /**
396
+ * Recurrence interval in days (default: 1).
397
+ * @example 3 // every third day
398
+ */
399
+ interval?: number;
400
+ /**
401
+ * Anchor date for interval phasing and the lower bound (YYYY-MM-DD).
402
+ * With `interval > 1`, occurrences land on `startDate`, `startDate + interval`, …
403
+ * @example "2025-01-06"
404
+ */
405
+ startDate?: string;
406
+ /**
407
+ * End date for the recurrence (YYYY-MM-DD, inclusive).
408
+ * @example "2025-12-31"
409
+ */
410
+ endDate?: string;
411
+ /**
412
+ * Specific dates to exclude (YYYY-MM-DD).
413
+ * @example ["2025-12-25"]
414
+ */
415
+ excludeDates?: string[];
416
+ }
375
417
  /**
376
418
  * FORMULA - Custom formula-based events
377
419
  * @template TMetadata - Custom metadata type extending Record<string, unknown>
@@ -434,11 +476,13 @@ interface NthWeekdayEvent<TMetadata extends Record<string, unknown> = Record<str
434
476
  * - `1`–`4`: 1st through 4th occurrence
435
477
  * - `-1`: last occurrence
436
478
  *
437
- * Required in simple mode (paired with `month`). Omit for anchored mode.
479
+ * Required in simple mode. Omit for anchored mode.
438
480
  */
439
481
  nth?: 1 | 2 | 3 | 4 | -1;
440
482
  /**
441
- * Month (1-12). Required in simple mode. Omit for anchored mode.
483
+ * Month (1-12). In simple mode, **omit to repeat every month** (e.g. the first
484
+ * Monday of every month); set it to pin one month (e.g. the 4th Thursday of
485
+ * November). Ignored in anchored mode.
442
486
  */
443
487
  month?: number;
444
488
  /**
@@ -488,7 +532,7 @@ interface RelativeEvent<TMetadata extends Record<string, unknown> = Record<strin
488
532
  weeks?: number;
489
533
  };
490
534
  }
491
- type CoreEventConfig<TMetadata extends Record<string, unknown> = Record<string, unknown>, TCategory extends string = string> = ConstEvent<TMetadata, TCategory> | FixedEvent<TMetadata, TCategory> | MonthlyEvent<TMetadata, TCategory> | WeeklyEvent<TMetadata, TCategory> | NthWeekdayEvent<TMetadata, TCategory> | FormulaEvent<TMetadata, TCategory> | RelativeEvent<TMetadata, TCategory>;
535
+ type CoreEventConfig<TMetadata extends Record<string, unknown> = Record<string, unknown>, TCategory extends string = string> = ConstEvent<TMetadata, TCategory> | FixedEvent<TMetadata, TCategory> | MonthlyEvent<TMetadata, TCategory> | WeeklyEvent<TMetadata, TCategory> | DailyEvent<TMetadata, TCategory> | NthWeekdayEvent<TMetadata, TCategory> | FormulaEvent<TMetadata, TCategory> | RelativeEvent<TMetadata, TCategory>;
492
536
  /**
493
537
  * Generic event config - extended by plugins.
494
538
  * Use the generic parameter to enforce metadata types.
@@ -513,7 +557,7 @@ type EventConfig<TMetadata extends Record<string, unknown> = Record<string, unkn
513
557
  /**
514
558
  * Event type identifiers
515
559
  */
516
- type CoreEventType = "const" | "fixed" | "monthly" | "weekly" | "nth-weekday" | "formula" | "relative";
560
+ type CoreEventType = "const" | "fixed" | "monthly" | "weekly" | "daily" | "nth-weekday" | "formula" | "relative";
517
561
  /**
518
562
  * Custom event configuration for overriding event properties.
519
563
  * Used by generator packages to allow customization of generated events.
@@ -587,4 +631,4 @@ type CustomEventConfig<TMetadata extends Record<string, unknown> = Record<string
587
631
  */
588
632
  type CustomEventResolver<TKey extends string, TMetadata extends Record<string, unknown> = Record<string, unknown>, TCategory extends string = string, TExclude extends keyof BaseEventProperties = never> = (key: TKey) => CustomEventConfig<TMetadata, TCategory, TExclude> | undefined;
589
633
 
590
- export type { BaseEventProperties as B, ConflictResolver as C, DateString as D, EventConfig as E, FixedEvent as F, MonthlyEvent as M, NthWeekdayEvent as N, OverrideDatesMap as O, RelativeEvent as R, WeekdayIndex as W, ConflictAction as a, ConflictMap as b, ConflictResolverFn as c, ConstEvent as d, CoreEventConfig as e, CoreEventType as f, CustomEventConfig as g, CustomEventResolver as h, DateAnchor as i, FormulaEvent as j, Reminder as k, WeeklyEvent as l };
634
+ export type { BaseEventProperties as B, ConflictResolver as C, DateString as D, EventConfig as E, FixedEvent as F, MonthlyEvent as M, NthWeekdayEvent as N, OverrideDatesMap as O, RelativeEvent as R, WeekdayIndex as W, ConflictAction as a, ConflictMap as b, ConflictResolverFn as c, ConstEvent as d, CoreEventConfig as e, CoreEventType as f, CustomEventConfig as g, CustomEventResolver as h, DailyEvent as i, DateAnchor as j, EventException as k, EventExceptions as l, FormulaEvent as m, Reminder as n, WeeklyEvent as o };
package/dist/index.cjs CHANGED
@@ -86,6 +86,14 @@ function getYearsInRange(from, to) {
86
86
  function createDate(year, month, day) {
87
87
  return new Date(year, month - 1, day);
88
88
  }
89
+ function getDaysInMonth(year, month) {
90
+ return new Date(year, month, 0).getDate();
91
+ }
92
+ function resolveDayOfMonth(day, year, month) {
93
+ if (day >= 0) return day;
94
+ const resolved = getDaysInMonth(year, month) + day + 1;
95
+ return resolved >= 1 ? resolved : null;
96
+ }
89
97
  function addDays(date, days) {
90
98
  const result = new Date(date);
91
99
  result.setDate(result.getDate() + days);
@@ -127,7 +135,7 @@ var constEventHandler = {
127
135
  validate(event) {
128
136
  if (typeof event !== "object" || event === null) return false;
129
137
  const e = event;
130
- return e.type === "const" && typeof e.month === "number" && e.month >= 1 && e.month <= 12 && typeof e.day === "number" && e.day >= 1 && e.day <= 31 && typeof e.title === "string" && typeof e.id === "string";
138
+ return e.type === "const" && typeof e.month === "number" && e.month >= 1 && e.month <= 12 && typeof e.day === "number" && e.day >= -31 && e.day <= 31 && e.day !== 0 && typeof e.title === "string" && typeof e.id === "string";
131
139
  },
132
140
  generate(event, year) {
133
141
  if (event.startYear !== void 0 && year < event.startYear) {
@@ -139,7 +147,9 @@ var constEventHandler = {
139
147
  if (event.excludeYears?.includes(year)) {
140
148
  return [];
141
149
  }
142
- return [createDate(year, event.month, event.day)];
150
+ const day = resolveDayOfMonth(event.day, year, event.month);
151
+ if (day === null) return [];
152
+ return [createDate(year, event.month, day)];
143
153
  }
144
154
  };
145
155
 
@@ -159,14 +169,11 @@ var fixedEventHandler = {
159
169
  };
160
170
 
161
171
  // src/generators/monthly.ts
162
- function getDaysInMonth(year, month) {
163
- return new Date(year, month, 0).getDate();
164
- }
165
172
  var monthlyEventHandler = {
166
173
  validate(event) {
167
174
  if (typeof event !== "object" || event === null) return false;
168
175
  const e = event;
169
- return e.type === "monthly" && typeof e.day === "number" && e.day >= 1 && e.day <= 31 && typeof e.title === "string" && typeof e.id === "string";
176
+ return e.type === "monthly" && typeof e.day === "number" && e.day >= -31 && e.day <= 31 && e.day !== 0 && typeof e.title === "string" && typeof e.id === "string";
170
177
  },
171
178
  generate(event, year) {
172
179
  const dates = [];
@@ -184,10 +191,11 @@ var monthlyEventHandler = {
184
191
  continue;
185
192
  }
186
193
  const daysInMonth = getDaysInMonth(year, month);
187
- if (event.day > daysInMonth) {
194
+ const day = resolveDayOfMonth(event.day, year, month);
195
+ if (day === null || day > daysInMonth) {
188
196
  continue;
189
197
  }
190
- const date = createDate(year, month, event.day);
198
+ const date = createDate(year, month, day);
191
199
  const dateStr = formatDate(date);
192
200
  if (event.excludeDates?.includes(dateStr)) {
193
201
  continue;
@@ -265,6 +273,49 @@ var weeklyEventHandler = {
265
273
  }
266
274
  };
267
275
 
276
+ // src/generators/daily.ts
277
+ var PHASE_EPOCH = createDate(1970, 1, 1);
278
+ var dailyEventHandler = {
279
+ validate(event) {
280
+ if (typeof event !== "object" || event === null) return false;
281
+ const e = event;
282
+ if (e.type !== "daily") return false;
283
+ if (typeof e.title !== "string" || typeof e.id !== "string") return false;
284
+ if (e.interval !== void 0 && (typeof e.interval !== "number" || e.interval < 1))
285
+ return false;
286
+ return true;
287
+ },
288
+ generate(event, year) {
289
+ const interval = event.interval ?? 1;
290
+ const yearEnd = createDate(year, 12, 31);
291
+ const startLimit = event.startDate ? parseDate(event.startDate) : null;
292
+ const endLimit = event.endDate ? parseDate(event.endDate) : null;
293
+ const anchor = startLimit ?? PHASE_EPOCH;
294
+ const dates = [];
295
+ let current = createDate(year, 1, 1);
296
+ while (current <= yearEnd) {
297
+ if (startLimit && current < startLimit) {
298
+ current = addDays(current, 1);
299
+ continue;
300
+ }
301
+ if (endLimit && current > endLimit) break;
302
+ if (interval > 1) {
303
+ const offset = daysBetween(anchor, current);
304
+ if (offset % interval !== 0) {
305
+ current = addDays(current, 1);
306
+ continue;
307
+ }
308
+ }
309
+ const dateStr = formatDate(current);
310
+ if (!event.excludeDates?.includes(dateStr)) {
311
+ dates.push(new Date(current));
312
+ }
313
+ current = addDays(current, 1);
314
+ }
315
+ return dates;
316
+ }
317
+ };
318
+
268
319
  // src/generators/nth-weekday.ts
269
320
  function nthWeekdayInMonth(year, month, dayOfWeek, nth) {
270
321
  if (nth === -1) {
@@ -307,7 +358,7 @@ var nthWeekdayEventHandler = {
307
358
  const hasAnchored = e.after !== void 0;
308
359
  if (hasSimple && !hasAnchored) {
309
360
  if (typeof e.nth !== "number" || ![1, 2, 3, 4, -1].includes(e.nth)) return false;
310
- if (typeof e.month !== "number" || e.month < 1 || e.month > 12)
361
+ if (e.month !== void 0 && (typeof e.month !== "number" || e.month < 1 || e.month > 12))
311
362
  return false;
312
363
  } else if (!hasAnchored) {
313
364
  return false;
@@ -331,9 +382,17 @@ var nthWeekdayEventHandler = {
331
382
  }
332
383
  return candidate ? [candidate] : [];
333
384
  }
334
- if (event.nth !== void 0 && event.month !== void 0) {
335
- const date = nthWeekdayInMonth(year, event.month, event.dayOfWeek, event.nth);
336
- return date ? [date] : [];
385
+ if (event.nth !== void 0) {
386
+ if (event.month !== void 0) {
387
+ const date = nthWeekdayInMonth(year, event.month, event.dayOfWeek, event.nth);
388
+ return date ? [date] : [];
389
+ }
390
+ const dates = [];
391
+ for (let month = 1; month <= 12; month++) {
392
+ const date = nthWeekdayInMonth(year, month, event.dayOfWeek, event.nth);
393
+ if (date) dates.push(date);
394
+ }
395
+ return dates;
337
396
  }
338
397
  return [];
339
398
  }
@@ -650,6 +709,27 @@ var SearchBuilder = class {
650
709
  this.options.hasAllCategories = categories;
651
710
  return this;
652
711
  }
712
+ /**
713
+ * Filter by event type(s) — match any (e.g. `"weekly"`, `"daily"`, a plugin type).
714
+ */
715
+ type(...types) {
716
+ this.options.types = types;
717
+ return this;
718
+ }
719
+ /**
720
+ * Filter by status — match any (e.g. hide cancelled by listing the ones you want).
721
+ */
722
+ status(...status) {
723
+ this.options.status = status;
724
+ return this;
725
+ }
726
+ /**
727
+ * Filter by source — match any (the plugin / feed / subscription that produced it).
728
+ */
729
+ source(...source) {
730
+ this.options.source = source;
731
+ return this;
732
+ }
653
733
  /**
654
734
  * Search by metadata key-value pairs.
655
735
  * Supports nested objects and case-insensitive string matching.
@@ -897,6 +977,15 @@ function executeSearch(events, options) {
897
977
  return options.hasAllCategories.every((cat) => e.categories.includes(cat));
898
978
  });
899
979
  }
980
+ if (options.types && options.types.length > 0) {
981
+ results = results.filter((e) => options.types.includes(e.type));
982
+ }
983
+ if (options.status && options.status.length > 0) {
984
+ results = results.filter((e) => e.status !== void 0 && options.status.includes(e.status));
985
+ }
986
+ if (options.source && options.source.length > 0) {
987
+ results = results.filter((e) => e.source !== void 0 && options.source.includes(e.source));
988
+ }
900
989
  if (options.metadata) {
901
990
  results = results.filter((e) => matchesMetadata(e.metadata, options.metadata));
902
991
  }
@@ -990,6 +1079,7 @@ var CalendaryInstance = class _CalendaryInstance {
990
1079
  this.eventTypeHandlers.set("fixed", fixedEventHandler);
991
1080
  this.eventTypeHandlers.set("monthly", monthlyEventHandler);
992
1081
  this.eventTypeHandlers.set("weekly", weeklyEventHandler);
1082
+ this.eventTypeHandlers.set("daily", dailyEventHandler);
993
1083
  this.eventTypeHandlers.set("nth-weekday", nthWeekdayEventHandler);
994
1084
  this.eventTypeHandlers.set(
995
1085
  "formula",
@@ -1086,6 +1176,27 @@ var CalendaryInstance = class _CalendaryInstance {
1086
1176
  events: c.events
1087
1177
  });
1088
1178
  }
1179
+ /**
1180
+ * Export events as a portable {@link Collection} — the inverse of {@link load}.
1181
+ * Serializes the plain event configs plus a manifest of the plugins their event
1182
+ * types need, so the result round-trips back through `load()`. Pass it to
1183
+ * `JSON.stringify` for a `.cdy` document.
1184
+ *
1185
+ * @param options.group - export a single group; omit to export every group's events.
1186
+ * @param options.name - the collection name (also the default group id on re-load).
1187
+ * @param options.version - an informational version stamp.
1188
+ */
1189
+ toCollection(options = {}) {
1190
+ const groups = options.group !== void 0 ? [this.groups.get(options.group)].filter((g) => g != null) : [...this.groups.values()];
1191
+ const events = groups.flatMap((g) => g.events);
1192
+ const usedTypes = new Set(events.map((e) => e.type));
1193
+ const plugins = [...this.plugins.values()].filter((p) => Object.keys(p.eventTypes ?? {}).some((t) => usedTypes.has(t))).map((p) => p.name);
1194
+ const out = { events };
1195
+ if (options.name) out.collection = options.name;
1196
+ if (options.version) out.version = options.version;
1197
+ if (plugins.length) out.plugins = plugins;
1198
+ return out;
1199
+ }
1089
1200
  removeGroup(groupId) {
1090
1201
  this.groups.delete(groupId);
1091
1202
  this.dirty = true;
@@ -1106,12 +1217,18 @@ var CalendaryInstance = class _CalendaryInstance {
1106
1217
  return this;
1107
1218
  }
1108
1219
  generateEvents(from, to) {
1220
+ if (this.dirty) {
1221
+ this.cache.invalidate();
1222
+ this.dirty = false;
1223
+ }
1109
1224
  const cacheKey = `${from}:${to}`;
1110
- if (!this.dirty && this.cache.has(cacheKey)) {
1225
+ if (this.cache.has(cacheKey)) {
1111
1226
  return this.cache.get(cacheKey);
1112
1227
  }
1113
1228
  const events = [];
1114
- const years = getYearsInRange(from, to);
1229
+ const inRange = getYearsInRange(from, to);
1230
+ const years = inRange.length ? [inRange[0] - 1, ...inRange, inRange[inRange.length - 1] + 1] : inRange;
1231
+ const seen = /* @__PURE__ */ new Set();
1115
1232
  for (const group of this.groups.values()) {
1116
1233
  if (!group.enabled) continue;
1117
1234
  for (const eventConfig of group.events) {
@@ -1124,6 +1241,10 @@ var CalendaryInstance = class _CalendaryInstance {
1124
1241
  let dateStr = formatDate(date);
1125
1242
  if (directives.startDate && dateStr < directives.startDate) continue;
1126
1243
  if (directives.endDate && dateStr > directives.endDate) continue;
1244
+ const occYear = +dateStr.slice(0, 4);
1245
+ if (directives.startYear !== void 0 && occYear < directives.startYear) continue;
1246
+ if (directives.endYear !== void 0 && occYear > directives.endYear) continue;
1247
+ if (directives.excludeYears?.includes(occYear)) continue;
1127
1248
  const exception = directives.exceptions?.[dateStr];
1128
1249
  if (exception && "skip" in exception) {
1129
1250
  continue;
@@ -1143,6 +1264,13 @@ var CalendaryInstance = class _CalendaryInstance {
1143
1264
  }
1144
1265
  }
1145
1266
  if (!isDateInRange(dateStr, from, to)) continue;
1267
+ const dedupeKey = JSON.stringify([
1268
+ group.id,
1269
+ eventConfig.id,
1270
+ dateStr
1271
+ ]);
1272
+ if (seen.has(dedupeKey)) continue;
1273
+ seen.add(dedupeKey);
1146
1274
  const calendarEvent = this.createCalendarEvent(
1147
1275
  eventConfig,
1148
1276
  group,
@@ -1161,13 +1289,13 @@ var CalendaryInstance = class _CalendaryInstance {
1161
1289
  });
1162
1290
  this.cache.set(cacheKey, events);
1163
1291
  this.index.index(events);
1164
- this.dirty = false;
1165
1292
  return events;
1166
1293
  }
1167
1294
  createCalendarEvent(config, group, dateStr, override) {
1168
1295
  const base = config;
1169
1296
  const event = {
1170
1297
  id: generateEventId(base.id, dateStr),
1298
+ type: base.type,
1171
1299
  sourceEventId: base.id,
1172
1300
  title: base.title,
1173
1301
  date: dateStr,
@@ -1223,13 +1351,16 @@ var CalendaryInstance = class _CalendaryInstance {
1223
1351
  * Get calendar days for a date range
1224
1352
  */
1225
1353
  getDays(options) {
1226
- const { groups } = options;
1354
+ const { groups, types } = options;
1227
1355
  const fromStr = normalizeDateInput(options.from);
1228
1356
  const toStr = normalizeDateInput(options.to);
1229
1357
  let events = this.generateEvents(fromStr, toStr);
1230
1358
  if (groups && groups.length > 0) {
1231
1359
  events = events.filter((e) => groups.includes(e.groupId));
1232
1360
  }
1361
+ if (types && types.length > 0) {
1362
+ events = events.filter((e) => types.includes(e.type));
1363
+ }
1233
1364
  const eventsByDate = /* @__PURE__ */ new Map();
1234
1365
  for (const event of events) {
1235
1366
  const existing = eventsByDate.get(event.date) || [];
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { E as EventConfig, B as BaseEventProperties, D as DateString, C as ConflictResolver, a as ConflictAction } from './events-Bm7R9XFB.cjs';
2
- export { b as ConflictMap, c as ConflictResolverFn, d as ConstEvent, e as CoreEventConfig, f as CoreEventType, g as CustomEventConfig, h as CustomEventResolver, i as DateAnchor, F as FixedEvent, j as FormulaEvent, M as MonthlyEvent, N as NthWeekdayEvent, O as OverrideDatesMap, R as RelativeEvent, k as Reminder, W as WeekdayIndex, l as WeeklyEvent } from './events-Bm7R9XFB.cjs';
1
+ import { E as EventConfig, B as BaseEventProperties, D as DateString, C as ConflictResolver, a as ConflictAction } from './events-DSiv9j6V.cjs';
2
+ export { b as ConflictMap, c as ConflictResolverFn, d as ConstEvent, e as CoreEventConfig, f as CoreEventType, g as CustomEventConfig, h as CustomEventResolver, i as DailyEvent, j as DateAnchor, k as EventException, l as EventExceptions, F as FixedEvent, m as FormulaEvent, M as MonthlyEvent, N as NthWeekdayEvent, O as OverrideDatesMap, R as RelativeEvent, n as Reminder, W as WeekdayIndex, o as WeeklyEvent } from './events-DSiv9j6V.cjs';
3
3
 
4
4
  /**
5
5
  * Group configuration
@@ -71,6 +71,11 @@ interface Group extends GroupConfig {
71
71
  * ```
72
72
  */
73
73
  interface CalendarEvent<TMetadata extends Record<string, unknown> = Record<string, unknown>, TCategory extends string = string> extends BaseEventProperties<TMetadata, TCategory> {
74
+ /**
75
+ * The originating event-config `type` (e.g. `"const"`, `"weekly"`, `"daily"`,
76
+ * or a plugin type). Lets a consumer group or filter occurrences by kind.
77
+ */
78
+ type: string;
74
79
  /** Original event ID from the event config */
75
80
  sourceEventId: string;
76
81
  /** Date in YYYY-MM-DD format */
@@ -269,6 +274,9 @@ interface SearchOptions {
269
274
  keywordsContain?: string[];
270
275
  categories?: string[];
271
276
  hasAllCategories?: string[];
277
+ types?: string[];
278
+ status?: ("confirmed" | "tentative" | "cancelled")[];
279
+ source?: string[];
272
280
  metadata?: MetadataQuery;
273
281
  minPriority?: number;
274
282
  maxPriority?: number;
@@ -368,6 +376,18 @@ declare class SearchBuilder<TMetadata extends Record<string, unknown> = Record<s
368
376
  * Filter by categories (must have all)
369
377
  */
370
378
  hasAllCategories(categories: string[]): this;
379
+ /**
380
+ * Filter by event type(s) — match any (e.g. `"weekly"`, `"daily"`, a plugin type).
381
+ */
382
+ type(...types: string[]): this;
383
+ /**
384
+ * Filter by status — match any (e.g. hide cancelled by listing the ones you want).
385
+ */
386
+ status(...status: ("confirmed" | "tentative" | "cancelled")[]): this;
387
+ /**
388
+ * Filter by source — match any (the plugin / feed / subscription that produced it).
389
+ */
390
+ source(...source: string[]): this;
371
391
  /**
372
392
  * Search by metadata key-value pairs.
373
393
  * Supports nested objects and case-insensitive string matching.
@@ -512,6 +532,21 @@ declare class CalendaryInstance<TMetadata extends Record<string, unknown> = Reco
512
532
  * Nothing is fetched or executed behind your back.
513
533
  */
514
534
  load(collection: Collection | string): this;
535
+ /**
536
+ * Export events as a portable {@link Collection} — the inverse of {@link load}.
537
+ * Serializes the plain event configs plus a manifest of the plugins their event
538
+ * types need, so the result round-trips back through `load()`. Pass it to
539
+ * `JSON.stringify` for a `.cdy` document.
540
+ *
541
+ * @param options.group - export a single group; omit to export every group's events.
542
+ * @param options.name - the collection name (also the default group id on re-load).
543
+ * @param options.version - an informational version stamp.
544
+ */
545
+ toCollection(options?: {
546
+ name?: string;
547
+ version?: string;
548
+ group?: string;
549
+ }): Collection;
515
550
  removeGroup(groupId: string): this;
516
551
  getGroup(groupId: string): Group | undefined;
517
552
  getGroups(): Group[];
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { E as EventConfig, B as BaseEventProperties, D as DateString, C as ConflictResolver, a as ConflictAction } from './events-Bm7R9XFB.js';
2
- export { b as ConflictMap, c as ConflictResolverFn, d as ConstEvent, e as CoreEventConfig, f as CoreEventType, g as CustomEventConfig, h as CustomEventResolver, i as DateAnchor, F as FixedEvent, j as FormulaEvent, M as MonthlyEvent, N as NthWeekdayEvent, O as OverrideDatesMap, R as RelativeEvent, k as Reminder, W as WeekdayIndex, l as WeeklyEvent } from './events-Bm7R9XFB.js';
1
+ import { E as EventConfig, B as BaseEventProperties, D as DateString, C as ConflictResolver, a as ConflictAction } from './events-DSiv9j6V.js';
2
+ export { b as ConflictMap, c as ConflictResolverFn, d as ConstEvent, e as CoreEventConfig, f as CoreEventType, g as CustomEventConfig, h as CustomEventResolver, i as DailyEvent, j as DateAnchor, k as EventException, l as EventExceptions, F as FixedEvent, m as FormulaEvent, M as MonthlyEvent, N as NthWeekdayEvent, O as OverrideDatesMap, R as RelativeEvent, n as Reminder, W as WeekdayIndex, o as WeeklyEvent } from './events-DSiv9j6V.js';
3
3
 
4
4
  /**
5
5
  * Group configuration
@@ -71,6 +71,11 @@ interface Group extends GroupConfig {
71
71
  * ```
72
72
  */
73
73
  interface CalendarEvent<TMetadata extends Record<string, unknown> = Record<string, unknown>, TCategory extends string = string> extends BaseEventProperties<TMetadata, TCategory> {
74
+ /**
75
+ * The originating event-config `type` (e.g. `"const"`, `"weekly"`, `"daily"`,
76
+ * or a plugin type). Lets a consumer group or filter occurrences by kind.
77
+ */
78
+ type: string;
74
79
  /** Original event ID from the event config */
75
80
  sourceEventId: string;
76
81
  /** Date in YYYY-MM-DD format */
@@ -269,6 +274,9 @@ interface SearchOptions {
269
274
  keywordsContain?: string[];
270
275
  categories?: string[];
271
276
  hasAllCategories?: string[];
277
+ types?: string[];
278
+ status?: ("confirmed" | "tentative" | "cancelled")[];
279
+ source?: string[];
272
280
  metadata?: MetadataQuery;
273
281
  minPriority?: number;
274
282
  maxPriority?: number;
@@ -368,6 +376,18 @@ declare class SearchBuilder<TMetadata extends Record<string, unknown> = Record<s
368
376
  * Filter by categories (must have all)
369
377
  */
370
378
  hasAllCategories(categories: string[]): this;
379
+ /**
380
+ * Filter by event type(s) — match any (e.g. `"weekly"`, `"daily"`, a plugin type).
381
+ */
382
+ type(...types: string[]): this;
383
+ /**
384
+ * Filter by status — match any (e.g. hide cancelled by listing the ones you want).
385
+ */
386
+ status(...status: ("confirmed" | "tentative" | "cancelled")[]): this;
387
+ /**
388
+ * Filter by source — match any (the plugin / feed / subscription that produced it).
389
+ */
390
+ source(...source: string[]): this;
371
391
  /**
372
392
  * Search by metadata key-value pairs.
373
393
  * Supports nested objects and case-insensitive string matching.
@@ -512,6 +532,21 @@ declare class CalendaryInstance<TMetadata extends Record<string, unknown> = Reco
512
532
  * Nothing is fetched or executed behind your back.
513
533
  */
514
534
  load(collection: Collection | string): this;
535
+ /**
536
+ * Export events as a portable {@link Collection} — the inverse of {@link load}.
537
+ * Serializes the plain event configs plus a manifest of the plugins their event
538
+ * types need, so the result round-trips back through `load()`. Pass it to
539
+ * `JSON.stringify` for a `.cdy` document.
540
+ *
541
+ * @param options.group - export a single group; omit to export every group's events.
542
+ * @param options.name - the collection name (also the default group id on re-load).
543
+ * @param options.version - an informational version stamp.
544
+ */
545
+ toCollection(options?: {
546
+ name?: string;
547
+ version?: string;
548
+ group?: string;
549
+ }): Collection;
515
550
  removeGroup(groupId: string): this;
516
551
  getGroup(groupId: string): Group | undefined;
517
552
  getGroups(): Group[];