@wirecat/cli-core 0.538.0 → 0.539.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.
@@ -62,6 +62,13 @@ const OTHERS = [
62
62
  validation("event.repeat-split", "the series cannot be split at this occurrence, or was split there already"),
63
63
  validation("event.repeat-count", "a COUNT series is split only with a rule for its new part"),
64
64
  validation("event.unmovable", "the provider keeps this kind of event on its calendar"),
65
+ validation("event.text-with-fields", "--text takes the whole event from its line, so no field option goes with it"),
66
+ validation("calendar.ambiguous", "two or more calendars have this name; name one by its id"),
67
+ validation("export.too-many", "the query finds more events than one export writes"),
68
+ validation("ics.not-icalendar", "the file is not iCalendar"),
69
+ validation("ics.no-events", "the file holds no event that can be imported"),
70
+ validation("ics.too-many", "the file holds more events than one import sends"),
71
+ validation("ics.too-large", "the file is larger than one import reads"),
65
72
  ["calendar.read-only", "permission_error", "the calendar takes no new events from this account"],
66
73
  ["calendar.unsynced", "not_found", "the calendar account has no synced calendar in the store"],
67
74
  ["calendar.sync-incomplete", "not_found", "a synced calendar's sync never finished or is mid-round"],
@@ -204,7 +204,13 @@ export interface EventFilter {
204
204
  accountId?: number;
205
205
  /** Several accounts' events at once, in place of `accountId`. */
206
206
  accountIds?: readonly number[];
207
+ /** Only this event. */
208
+ id?: number;
207
209
  calendarId?: number;
210
+ /** Events with a copy, in the accounts read, of this kind; a copy stored with no kind is `default`. */
211
+ eventType?: EntryEventType;
212
+ /** Events with no copy, in the accounts read, of any of these kinds. */
213
+ notEventTypes?: readonly EntryEventType[];
208
214
  /** Events whose entries list one of this person's contacts. */
209
215
  personId?: number;
210
216
  /** Latest start first; earliest first when absent. */
@@ -383,6 +383,21 @@ export const eventStoreContract = (make) => {
383
383
  assert.equal((await store.events({ ...window, accountIds: [fixture.accountId, fixture.otherAccountId] })).length, 3);
384
384
  assert.equal((await store.events({ ...window, accountIds: [fixture.otherAccountId] })).length, 1);
385
385
  }),
386
+ test("events filter by the kind of their copies; a copy with no kind is default", async ({ store, calendar }) => {
387
+ const at = later;
388
+ await store.saveEntries(calendar, [
389
+ single("meeting", at, { title: "meeting" }),
390
+ single("plain", at + HOUR, { title: "plain", eventType: "default" }),
391
+ single("cake", at + 2 * HOUR, { title: "cake", eventType: "birthday" }),
392
+ single("deep", at + 3 * HOUR, { title: "deep", eventType: "focus" }),
393
+ ], 2);
394
+ const window = { from: at - HOUR, to: at + 8 * HOUR };
395
+ const titles = async (filter) => (await store.events({ ...window, ...filter })).map(({ title }) => title);
396
+ assert.deepEqual(await titles({ eventType: "default" }), ["meeting", "plain"]);
397
+ assert.deepEqual(await titles({ eventType: "birthday" }), ["cake"]);
398
+ assert.deepEqual(await titles({ notEventTypes: ["birthday", "focus"] }), ["meeting", "plain"]);
399
+ assert.deepEqual(await titles({ eventType: "default", notEventTypes: ["default"] }), []);
400
+ }),
386
401
  test("participants are kept by address; a room is listed and is no contact", async ({ store, calendar }) => {
387
402
  const [entry] = await store.saveEntries(calendar, [
388
403
  single("a", later, {
@@ -43,6 +43,10 @@ export const listEvents = (database, filter) => {
43
43
  clauses.push("e.starts_at >= ?");
44
44
  params.push(filter.startsFrom - DAY);
45
45
  }
46
+ if (filter.id !== undefined) {
47
+ clauses.push("e.id = ?");
48
+ params.push(filter.id);
49
+ }
46
50
  if (filter.calendarId !== undefined) {
47
51
  clauses.push(entries("ce.calendar_id = ?"));
48
52
  params.push(filter.calendarId);
@@ -57,6 +61,17 @@ export const listEvents = (database, filter) => {
57
61
  };
58
62
  if (accounts !== undefined)
59
63
  clauses.push(entries(inAccounts("c.account_id")));
64
+ // A copy's kind counts only in the accounts read: another account's copy of the same meeting is its own.
65
+ const ofKind = (kinds) => {
66
+ for (const kind of kinds)
67
+ params.push(kind);
68
+ const scope = accounts === undefined ? "" : ` AND ${inAccounts("c.account_id")}`;
69
+ return entries(`coalesce(ce.event_type, 'default') IN (${kinds.map(() => "?").join(", ")})${scope}`);
70
+ };
71
+ if (filter.eventType !== undefined)
72
+ clauses.push(ofKind([filter.eventType]));
73
+ if (filter.notEventTypes?.length)
74
+ clauses.push(`NOT ${ofKind(filter.notEventTypes)}`);
60
75
  if (filter.personId !== undefined) {
61
76
  clauses.push(entries("EXISTS (SELECT 1 FROM calendar_entry_participants p JOIN identity_links il ON il.identity_id = p.identity_id " +
62
77
  "WHERE p.entry_id = ce.id AND il.person_id = ?)"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.538.0",
3
+ "version": "0.539.0",
4
4
  "description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Viacheslav Ptsarev",