@hasna/calendar 0.1.8 → 0.1.9
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.
- package/dist/cli/index.js +84 -47
- package/dist/db/attendees.d.ts.map +1 -1
- package/dist/db/event-time.d.ts +9 -0
- package/dist/db/event-time.d.ts.map +1 -0
- package/dist/db/events.d.ts.map +1 -1
- package/dist/index.js +91 -54
- package/dist/mcp/index.js +87 -48
- package/dist/server/index.js +87 -48
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -3241,32 +3241,22 @@ function deleteCalendar(id, db) {
|
|
|
3241
3241
|
return result.changes > 0;
|
|
3242
3242
|
}
|
|
3243
3243
|
|
|
3244
|
-
// src/db/
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
visibility: row.visibility,
|
|
3260
|
-
recurrence_rule: row.recurrence_rule,
|
|
3261
|
-
recurrence_exception_dates: row.recurrence_exception_dates ? JSON.parse(row.recurrence_exception_dates) : null,
|
|
3262
|
-
source_task_id: row.source_task_id,
|
|
3263
|
-
created_by: row.created_by,
|
|
3264
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
3265
|
-
created_at: row.created_at,
|
|
3266
|
-
updated_at: row.updated_at
|
|
3267
|
-
};
|
|
3244
|
+
// src/db/event-time.ts
|
|
3245
|
+
var ISO_DATE_TIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/;
|
|
3246
|
+
var NS_PER_MS = 1000000n;
|
|
3247
|
+
var NS_PER_MINUTE = 60000000000n;
|
|
3248
|
+
function daysInMonth(year, month) {
|
|
3249
|
+
const date = new Date(0);
|
|
3250
|
+
date.setUTCFullYear(year, month, 0);
|
|
3251
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
3252
|
+
return date.getUTCDate();
|
|
3253
|
+
}
|
|
3254
|
+
function timestampBaseMs(year, month, day, hour, minute, second) {
|
|
3255
|
+
const date = new Date(0);
|
|
3256
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
3257
|
+
date.setUTCHours(hour, minute, second, 0);
|
|
3258
|
+
return date.getTime();
|
|
3268
3259
|
}
|
|
3269
|
-
var ISO_DATE_TIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?(Z|[+-]\d{2}:\d{2})$/;
|
|
3270
3260
|
function parseEventTimestamp(value) {
|
|
3271
3261
|
const match = ISO_DATE_TIME_RE.exec(value);
|
|
3272
3262
|
if (!match) {
|
|
@@ -3278,23 +3268,28 @@ function parseEventTimestamp(value) {
|
|
|
3278
3268
|
const hour = Number(match[4]);
|
|
3279
3269
|
const minute = Number(match[5]);
|
|
3280
3270
|
const second = Number(match[6]);
|
|
3281
|
-
const
|
|
3282
|
-
const
|
|
3271
|
+
const fraction = match[7] || "";
|
|
3272
|
+
const offset = match[8];
|
|
3273
|
+
const maxDay = month >= 1 && month <= 12 ? daysInMonth(year, month) : 0;
|
|
3283
3274
|
if (month < 1 || month > 12 || day < 1 || day > maxDay || hour > 23 || minute > 59 || second > 59) {
|
|
3284
3275
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
3285
3276
|
}
|
|
3277
|
+
let offsetMinutes = 0;
|
|
3286
3278
|
if (offset !== "Z") {
|
|
3279
|
+
const offsetSign = offset[0] === "-" ? -1 : 1;
|
|
3287
3280
|
const offsetHour = Number(offset.slice(1, 3));
|
|
3288
3281
|
const offsetMinute = Number(offset.slice(4, 6));
|
|
3289
3282
|
if (offsetHour > 23 || offsetMinute > 59) {
|
|
3290
3283
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
3291
3284
|
}
|
|
3285
|
+
offsetMinutes = offsetSign * (offsetHour * 60 + offsetMinute);
|
|
3292
3286
|
}
|
|
3293
|
-
const timestamp =
|
|
3287
|
+
const timestamp = timestampBaseMs(year, month, day, hour, minute, second);
|
|
3294
3288
|
if (!Number.isFinite(timestamp)) {
|
|
3295
3289
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
3296
3290
|
}
|
|
3297
|
-
|
|
3291
|
+
const fractionalNs = BigInt(fraction.padEnd(9, "0") || "0");
|
|
3292
|
+
return BigInt(timestamp) * NS_PER_MS + fractionalNs - BigInt(offsetMinutes) * NS_PER_MINUTE;
|
|
3298
3293
|
}
|
|
3299
3294
|
function assertEventEndsAfterStart(startAt, endAt) {
|
|
3300
3295
|
const start = parseEventTimestamp(startAt);
|
|
@@ -3303,6 +3298,53 @@ function assertEventEndsAfterStart(startAt, endAt) {
|
|
|
3303
3298
|
throw new RangeError("Event end_at must be after start_at");
|
|
3304
3299
|
}
|
|
3305
3300
|
}
|
|
3301
|
+
function parseTimeRange(startAt, endAt) {
|
|
3302
|
+
const start = parseEventTimestamp(startAt);
|
|
3303
|
+
const end = parseEventTimestamp(endAt);
|
|
3304
|
+
if (end <= start) {
|
|
3305
|
+
throw new RangeError("Time range end must be after start");
|
|
3306
|
+
}
|
|
3307
|
+
return { start, end };
|
|
3308
|
+
}
|
|
3309
|
+
function compareEventInstants(a, b) {
|
|
3310
|
+
if (a < b)
|
|
3311
|
+
return -1;
|
|
3312
|
+
if (a > b)
|
|
3313
|
+
return 1;
|
|
3314
|
+
return 0;
|
|
3315
|
+
}
|
|
3316
|
+
function compareEventTimestampStrings(a, b) {
|
|
3317
|
+
return compareEventInstants(parseEventTimestamp(a), parseEventTimestamp(b)) || a.localeCompare(b);
|
|
3318
|
+
}
|
|
3319
|
+
|
|
3320
|
+
// src/db/events.ts
|
|
3321
|
+
function rowToEvent(row) {
|
|
3322
|
+
return {
|
|
3323
|
+
id: row.id,
|
|
3324
|
+
calendar_id: row.calendar_id,
|
|
3325
|
+
org_id: row.org_id,
|
|
3326
|
+
title: row.title,
|
|
3327
|
+
description: row.description,
|
|
3328
|
+
location: row.location,
|
|
3329
|
+
start_at: row.start_at,
|
|
3330
|
+
end_at: row.end_at,
|
|
3331
|
+
all_day: !!row.all_day,
|
|
3332
|
+
timezone: row.timezone,
|
|
3333
|
+
status: row.status,
|
|
3334
|
+
busy_type: row.busy_type,
|
|
3335
|
+
visibility: row.visibility,
|
|
3336
|
+
recurrence_rule: row.recurrence_rule,
|
|
3337
|
+
recurrence_exception_dates: row.recurrence_exception_dates ? JSON.parse(row.recurrence_exception_dates) : null,
|
|
3338
|
+
source_task_id: row.source_task_id,
|
|
3339
|
+
created_by: row.created_by,
|
|
3340
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
3341
|
+
created_at: row.created_at,
|
|
3342
|
+
updated_at: row.updated_at
|
|
3343
|
+
};
|
|
3344
|
+
}
|
|
3345
|
+
function positiveInteger(value) {
|
|
3346
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
3347
|
+
}
|
|
3306
3348
|
function createEvent2(input, db) {
|
|
3307
3349
|
db = db || getDatabase();
|
|
3308
3350
|
const id = crypto.randomUUID().slice(0, 8);
|
|
@@ -3320,6 +3362,8 @@ function listEvents(filter = {}, db) {
|
|
|
3320
3362
|
db = db || getDatabase();
|
|
3321
3363
|
const conditions = [];
|
|
3322
3364
|
const params = [];
|
|
3365
|
+
const after = filter.after ? parseEventTimestamp(filter.after) : null;
|
|
3366
|
+
const before = filter.before ? parseEventTimestamp(filter.before) : null;
|
|
3323
3367
|
if (filter.calendar_id) {
|
|
3324
3368
|
conditions.push("calendar_id = ?");
|
|
3325
3369
|
params.push(filter.calendar_id);
|
|
@@ -3332,14 +3376,6 @@ function listEvents(filter = {}, db) {
|
|
|
3332
3376
|
conditions.push("status = ?");
|
|
3333
3377
|
params.push(filter.status);
|
|
3334
3378
|
}
|
|
3335
|
-
if (filter.after) {
|
|
3336
|
-
conditions.push("start_at >= ?");
|
|
3337
|
-
params.push(filter.after);
|
|
3338
|
-
}
|
|
3339
|
-
if (filter.before) {
|
|
3340
|
-
conditions.push("start_at <= ?");
|
|
3341
|
-
params.push(filter.before);
|
|
3342
|
-
}
|
|
3343
3379
|
if (filter.created_by) {
|
|
3344
3380
|
conditions.push("created_by = ?");
|
|
3345
3381
|
params.push(filter.created_by);
|
|
@@ -3349,10 +3385,11 @@ function listEvents(filter = {}, db) {
|
|
|
3349
3385
|
params.push(filter.source_task_id);
|
|
3350
3386
|
}
|
|
3351
3387
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3352
|
-
const
|
|
3353
|
-
const
|
|
3354
|
-
const
|
|
3355
|
-
|
|
3388
|
+
const rows = db.query(`SELECT * FROM events ${where}`).all(...params);
|
|
3389
|
+
const events = rows.map(rowToEvent).map((event) => ({ event, start: parseEventTimestamp(event.start_at) })).filter(({ start }) => (after === null || start >= after) && (before === null || start <= before)).sort((a, b) => compareEventInstants(a.start, b.start) || a.event.start_at.localeCompare(b.event.start_at)).map(({ event }) => event);
|
|
3390
|
+
const offset = positiveInteger(filter.offset) || 0;
|
|
3391
|
+
const limit = positiveInteger(filter.limit);
|
|
3392
|
+
return limit ? events.slice(offset, offset + limit) : events.slice(offset);
|
|
3356
3393
|
}
|
|
3357
3394
|
function updateEvent(id, input, db) {
|
|
3358
3395
|
db = db || getDatabase();
|
|
@@ -3373,18 +3410,18 @@ function deleteEvent(id, db) {
|
|
|
3373
3410
|
function findConflicts(calendarId, range, excludeEventId, db) {
|
|
3374
3411
|
db = db || getDatabase();
|
|
3375
3412
|
const exclude = excludeEventId ? "AND id != ?" : "";
|
|
3376
|
-
const params = excludeEventId ? [calendarId,
|
|
3377
|
-
const
|
|
3378
|
-
|
|
3413
|
+
const params = excludeEventId ? [calendarId, excludeEventId] : [calendarId];
|
|
3414
|
+
const { start: rangeStart, end: rangeEnd } = parseTimeRange(range.start, range.end);
|
|
3415
|
+
const rows = db.query(`SELECT * FROM events WHERE calendar_id = ? AND status != 'cancelled' ${exclude}`).all(...params);
|
|
3416
|
+
return rows.map(rowToEvent).map((event) => ({ event, start: parseEventTimestamp(event.start_at), end: parseEventTimestamp(event.end_at) })).filter(({ start, end }) => start < rangeEnd && end > rangeStart).sort((a, b) => compareEventInstants(a.start, b.start) || a.event.start_at.localeCompare(b.event.start_at)).map(({ event }) => event);
|
|
3379
3417
|
}
|
|
3380
3418
|
function searchEvents(query, orgId, db) {
|
|
3381
3419
|
db = db || getDatabase();
|
|
3382
3420
|
const rows = db.query(`SELECT e.* FROM events e
|
|
3383
3421
|
INNER JOIN events_fts f ON f.rowid = e.rowid
|
|
3384
3422
|
WHERE events_fts MATCH ?
|
|
3385
|
-
${orgId ? "AND e.org_id = ?" : ""}
|
|
3386
|
-
|
|
3387
|
-
return rows.map(rowToEvent);
|
|
3423
|
+
${orgId ? "AND e.org_id = ?" : ""}`).all(query, ...orgId ? [orgId] : []);
|
|
3424
|
+
return rows.map(rowToEvent).sort((a, b) => compareEventTimestampStrings(a.start_at, b.start_at));
|
|
3388
3425
|
}
|
|
3389
3426
|
|
|
3390
3427
|
// src/db/attendees.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attendees.d.ts","sourceRoot":"","sources":["../../src/db/attendees.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"attendees.d.ts","sourceRoot":"","sources":["../../src/db/attendees.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAkBjG,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,aAAa,CAUvF;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,aAAa,GAAG,IAAI,CAI3E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,aAAa,EAAE,CAIpF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,aAAa,EAAE,CAUjF;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,aAAa,CAYnG;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAIjE;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAIhF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function parseEventTimestamp(value: string): bigint;
|
|
2
|
+
export declare function assertEventEndsAfterStart(startAt: string, endAt: string): void;
|
|
3
|
+
export declare function parseTimeRange(startAt: string, endAt: string): {
|
|
4
|
+
start: bigint;
|
|
5
|
+
end: bigint;
|
|
6
|
+
};
|
|
7
|
+
export declare function compareEventInstants(a: bigint, b: bigint): number;
|
|
8
|
+
export declare function compareEventTimestampStrings(a: string, b: string): number;
|
|
9
|
+
//# sourceMappingURL=event-time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-time.d.ts","sourceRoot":"","sources":["../../src/db/event-time.ts"],"names":[],"mappings":"AAkBA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAsCzD;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAO9E;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAS7F;AAED,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAIjE;AAED,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzE"}
|
package/dist/db/events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/db/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/db/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAgCnF,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,CAYzE;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,IAAI,CAIhE;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,UAAU,CAAC,MAAM,GAAE,gBAAqB,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAyBhF;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,CAcrF;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAI9D;AAID,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAiBnH;AAED,0FAA0F;AAC1F,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAkBrH;AAID,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAYlF"}
|
package/dist/index.js
CHANGED
|
@@ -518,32 +518,22 @@ function deleteCalendar(id, db) {
|
|
|
518
518
|
const result = db.run(`DELETE FROM calendars WHERE id = ?`, [id]);
|
|
519
519
|
return result.changes > 0;
|
|
520
520
|
}
|
|
521
|
-
// src/db/
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
visibility: row.visibility,
|
|
537
|
-
recurrence_rule: row.recurrence_rule,
|
|
538
|
-
recurrence_exception_dates: row.recurrence_exception_dates ? JSON.parse(row.recurrence_exception_dates) : null,
|
|
539
|
-
source_task_id: row.source_task_id,
|
|
540
|
-
created_by: row.created_by,
|
|
541
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
542
|
-
created_at: row.created_at,
|
|
543
|
-
updated_at: row.updated_at
|
|
544
|
-
};
|
|
521
|
+
// src/db/event-time.ts
|
|
522
|
+
var ISO_DATE_TIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/;
|
|
523
|
+
var NS_PER_MS = 1000000n;
|
|
524
|
+
var NS_PER_MINUTE = 60000000000n;
|
|
525
|
+
function daysInMonth(year, month) {
|
|
526
|
+
const date = new Date(0);
|
|
527
|
+
date.setUTCFullYear(year, month, 0);
|
|
528
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
529
|
+
return date.getUTCDate();
|
|
530
|
+
}
|
|
531
|
+
function timestampBaseMs(year, month, day, hour, minute, second) {
|
|
532
|
+
const date = new Date(0);
|
|
533
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
534
|
+
date.setUTCHours(hour, minute, second, 0);
|
|
535
|
+
return date.getTime();
|
|
545
536
|
}
|
|
546
|
-
var ISO_DATE_TIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?(Z|[+-]\d{2}:\d{2})$/;
|
|
547
537
|
function parseEventTimestamp(value) {
|
|
548
538
|
const match = ISO_DATE_TIME_RE.exec(value);
|
|
549
539
|
if (!match) {
|
|
@@ -555,23 +545,28 @@ function parseEventTimestamp(value) {
|
|
|
555
545
|
const hour = Number(match[4]);
|
|
556
546
|
const minute = Number(match[5]);
|
|
557
547
|
const second = Number(match[6]);
|
|
558
|
-
const
|
|
559
|
-
const
|
|
548
|
+
const fraction = match[7] || "";
|
|
549
|
+
const offset = match[8];
|
|
550
|
+
const maxDay = month >= 1 && month <= 12 ? daysInMonth(year, month) : 0;
|
|
560
551
|
if (month < 1 || month > 12 || day < 1 || day > maxDay || hour > 23 || minute > 59 || second > 59) {
|
|
561
552
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
562
553
|
}
|
|
554
|
+
let offsetMinutes = 0;
|
|
563
555
|
if (offset !== "Z") {
|
|
556
|
+
const offsetSign = offset[0] === "-" ? -1 : 1;
|
|
564
557
|
const offsetHour = Number(offset.slice(1, 3));
|
|
565
558
|
const offsetMinute = Number(offset.slice(4, 6));
|
|
566
559
|
if (offsetHour > 23 || offsetMinute > 59) {
|
|
567
560
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
568
561
|
}
|
|
562
|
+
offsetMinutes = offsetSign * (offsetHour * 60 + offsetMinute);
|
|
569
563
|
}
|
|
570
|
-
const timestamp =
|
|
564
|
+
const timestamp = timestampBaseMs(year, month, day, hour, minute, second);
|
|
571
565
|
if (!Number.isFinite(timestamp)) {
|
|
572
566
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
573
567
|
}
|
|
574
|
-
|
|
568
|
+
const fractionalNs = BigInt(fraction.padEnd(9, "0") || "0");
|
|
569
|
+
return BigInt(timestamp) * NS_PER_MS + fractionalNs - BigInt(offsetMinutes) * NS_PER_MINUTE;
|
|
575
570
|
}
|
|
576
571
|
function assertEventEndsAfterStart(startAt, endAt) {
|
|
577
572
|
const start = parseEventTimestamp(startAt);
|
|
@@ -580,6 +575,53 @@ function assertEventEndsAfterStart(startAt, endAt) {
|
|
|
580
575
|
throw new RangeError("Event end_at must be after start_at");
|
|
581
576
|
}
|
|
582
577
|
}
|
|
578
|
+
function parseTimeRange(startAt, endAt) {
|
|
579
|
+
const start = parseEventTimestamp(startAt);
|
|
580
|
+
const end = parseEventTimestamp(endAt);
|
|
581
|
+
if (end <= start) {
|
|
582
|
+
throw new RangeError("Time range end must be after start");
|
|
583
|
+
}
|
|
584
|
+
return { start, end };
|
|
585
|
+
}
|
|
586
|
+
function compareEventInstants(a, b) {
|
|
587
|
+
if (a < b)
|
|
588
|
+
return -1;
|
|
589
|
+
if (a > b)
|
|
590
|
+
return 1;
|
|
591
|
+
return 0;
|
|
592
|
+
}
|
|
593
|
+
function compareEventTimestampStrings(a, b) {
|
|
594
|
+
return compareEventInstants(parseEventTimestamp(a), parseEventTimestamp(b)) || a.localeCompare(b);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// src/db/events.ts
|
|
598
|
+
function rowToEvent(row) {
|
|
599
|
+
return {
|
|
600
|
+
id: row.id,
|
|
601
|
+
calendar_id: row.calendar_id,
|
|
602
|
+
org_id: row.org_id,
|
|
603
|
+
title: row.title,
|
|
604
|
+
description: row.description,
|
|
605
|
+
location: row.location,
|
|
606
|
+
start_at: row.start_at,
|
|
607
|
+
end_at: row.end_at,
|
|
608
|
+
all_day: !!row.all_day,
|
|
609
|
+
timezone: row.timezone,
|
|
610
|
+
status: row.status,
|
|
611
|
+
busy_type: row.busy_type,
|
|
612
|
+
visibility: row.visibility,
|
|
613
|
+
recurrence_rule: row.recurrence_rule,
|
|
614
|
+
recurrence_exception_dates: row.recurrence_exception_dates ? JSON.parse(row.recurrence_exception_dates) : null,
|
|
615
|
+
source_task_id: row.source_task_id,
|
|
616
|
+
created_by: row.created_by,
|
|
617
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
618
|
+
created_at: row.created_at,
|
|
619
|
+
updated_at: row.updated_at
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
function positiveInteger(value) {
|
|
623
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
624
|
+
}
|
|
583
625
|
function createEvent(input, db) {
|
|
584
626
|
db = db || getDatabase();
|
|
585
627
|
const id = crypto.randomUUID().slice(0, 8);
|
|
@@ -597,6 +639,8 @@ function listEvents(filter = {}, db) {
|
|
|
597
639
|
db = db || getDatabase();
|
|
598
640
|
const conditions = [];
|
|
599
641
|
const params = [];
|
|
642
|
+
const after = filter.after ? parseEventTimestamp(filter.after) : null;
|
|
643
|
+
const before = filter.before ? parseEventTimestamp(filter.before) : null;
|
|
600
644
|
if (filter.calendar_id) {
|
|
601
645
|
conditions.push("calendar_id = ?");
|
|
602
646
|
params.push(filter.calendar_id);
|
|
@@ -609,14 +653,6 @@ function listEvents(filter = {}, db) {
|
|
|
609
653
|
conditions.push("status = ?");
|
|
610
654
|
params.push(filter.status);
|
|
611
655
|
}
|
|
612
|
-
if (filter.after) {
|
|
613
|
-
conditions.push("start_at >= ?");
|
|
614
|
-
params.push(filter.after);
|
|
615
|
-
}
|
|
616
|
-
if (filter.before) {
|
|
617
|
-
conditions.push("start_at <= ?");
|
|
618
|
-
params.push(filter.before);
|
|
619
|
-
}
|
|
620
656
|
if (filter.created_by) {
|
|
621
657
|
conditions.push("created_by = ?");
|
|
622
658
|
params.push(filter.created_by);
|
|
@@ -626,10 +662,11 @@ function listEvents(filter = {}, db) {
|
|
|
626
662
|
params.push(filter.source_task_id);
|
|
627
663
|
}
|
|
628
664
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
629
|
-
const
|
|
630
|
-
const
|
|
631
|
-
const
|
|
632
|
-
|
|
665
|
+
const rows = db.query(`SELECT * FROM events ${where}`).all(...params);
|
|
666
|
+
const events = rows.map(rowToEvent).map((event) => ({ event, start: parseEventTimestamp(event.start_at) })).filter(({ start }) => (after === null || start >= after) && (before === null || start <= before)).sort((a, b) => compareEventInstants(a.start, b.start) || a.event.start_at.localeCompare(b.event.start_at)).map(({ event }) => event);
|
|
667
|
+
const offset = positiveInteger(filter.offset) || 0;
|
|
668
|
+
const limit = positiveInteger(filter.limit);
|
|
669
|
+
return limit ? events.slice(offset, offset + limit) : events.slice(offset);
|
|
633
670
|
}
|
|
634
671
|
function updateEvent(id, input, db) {
|
|
635
672
|
db = db || getDatabase();
|
|
@@ -650,28 +687,28 @@ function deleteEvent(id, db) {
|
|
|
650
687
|
function findConflicts(calendarId, range, excludeEventId, db) {
|
|
651
688
|
db = db || getDatabase();
|
|
652
689
|
const exclude = excludeEventId ? "AND id != ?" : "";
|
|
653
|
-
const params = excludeEventId ? [calendarId,
|
|
654
|
-
const
|
|
655
|
-
|
|
690
|
+
const params = excludeEventId ? [calendarId, excludeEventId] : [calendarId];
|
|
691
|
+
const { start: rangeStart, end: rangeEnd } = parseTimeRange(range.start, range.end);
|
|
692
|
+
const rows = db.query(`SELECT * FROM events WHERE calendar_id = ? AND status != 'cancelled' ${exclude}`).all(...params);
|
|
693
|
+
return rows.map(rowToEvent).map((event) => ({ event, start: parseEventTimestamp(event.start_at), end: parseEventTimestamp(event.end_at) })).filter(({ start, end }) => start < rangeEnd && end > rangeStart).sort((a, b) => compareEventInstants(a.start, b.start) || a.event.start_at.localeCompare(b.event.start_at)).map(({ event }) => event);
|
|
656
694
|
}
|
|
657
695
|
function findAgentConflicts(agentId, range, excludeEventId, db) {
|
|
658
696
|
db = db || getDatabase();
|
|
659
697
|
const exclude = excludeEventId ? "AND e.id != ?" : "";
|
|
660
|
-
const params = excludeEventId ? [agentId,
|
|
698
|
+
const params = excludeEventId ? [agentId, excludeEventId] : [agentId];
|
|
699
|
+
const { start: rangeStart, end: rangeEnd } = parseTimeRange(range.start, range.end);
|
|
661
700
|
const rows = db.query(`SELECT e.* FROM events e
|
|
662
701
|
INNER JOIN event_attendees a ON a.event_id = e.id
|
|
663
|
-
WHERE a.agent_id = ? AND e.
|
|
664
|
-
|
|
665
|
-
return rows.map(rowToEvent);
|
|
702
|
+
WHERE a.agent_id = ? AND e.status != 'cancelled' ${exclude}`).all(...params);
|
|
703
|
+
return rows.map(rowToEvent).map((event) => ({ event, start: parseEventTimestamp(event.start_at), end: parseEventTimestamp(event.end_at) })).filter(({ start, end }) => start < rangeEnd && end > rangeStart).sort((a, b) => compareEventInstants(a.start, b.start) || a.event.start_at.localeCompare(b.event.start_at)).map(({ event }) => event);
|
|
666
704
|
}
|
|
667
705
|
function searchEvents(query, orgId, db) {
|
|
668
706
|
db = db || getDatabase();
|
|
669
707
|
const rows = db.query(`SELECT e.* FROM events e
|
|
670
708
|
INNER JOIN events_fts f ON f.rowid = e.rowid
|
|
671
709
|
WHERE events_fts MATCH ?
|
|
672
|
-
${orgId ? "AND e.org_id = ?" : ""}
|
|
673
|
-
|
|
674
|
-
return rows.map(rowToEvent);
|
|
710
|
+
${orgId ? "AND e.org_id = ?" : ""}`).all(query, ...orgId ? [orgId] : []);
|
|
711
|
+
return rows.map(rowToEvent).sort((a, b) => compareEventTimestampStrings(a.start_at, b.start_at));
|
|
675
712
|
}
|
|
676
713
|
// src/db/attendees.ts
|
|
677
714
|
function rowToAttendee(row) {
|
|
@@ -706,10 +743,10 @@ function getAttendeesForEvent(eventId, db) {
|
|
|
706
743
|
}
|
|
707
744
|
function getEventsForAgent(agentId, db) {
|
|
708
745
|
db = db || getDatabase();
|
|
709
|
-
const rows = db.query(`SELECT a
|
|
746
|
+
const rows = db.query(`SELECT a.*, e.start_at AS event_start_at FROM event_attendees a
|
|
710
747
|
INNER JOIN events e ON e.id = a.event_id
|
|
711
|
-
WHERE a.agent_id = ? AND e.status != 'cancelled'
|
|
712
|
-
|
|
748
|
+
WHERE a.agent_id = ? AND e.status != 'cancelled'`).all(agentId);
|
|
749
|
+
rows.sort((a, b) => compareEventTimestampStrings(a.event_start_at, b.event_start_at));
|
|
713
750
|
return rows.map(rowToAttendee);
|
|
714
751
|
}
|
|
715
752
|
function updateAttendee(id, input, db) {
|
package/dist/mcp/index.js
CHANGED
|
@@ -4423,30 +4423,18 @@ var init_calendars = __esm(() => {
|
|
|
4423
4423
|
init_types2();
|
|
4424
4424
|
});
|
|
4425
4425
|
|
|
4426
|
-
// src/db/
|
|
4427
|
-
function
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
timezone: row.timezone,
|
|
4439
|
-
status: row.status,
|
|
4440
|
-
busy_type: row.busy_type,
|
|
4441
|
-
visibility: row.visibility,
|
|
4442
|
-
recurrence_rule: row.recurrence_rule,
|
|
4443
|
-
recurrence_exception_dates: row.recurrence_exception_dates ? JSON.parse(row.recurrence_exception_dates) : null,
|
|
4444
|
-
source_task_id: row.source_task_id,
|
|
4445
|
-
created_by: row.created_by,
|
|
4446
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
4447
|
-
created_at: row.created_at,
|
|
4448
|
-
updated_at: row.updated_at
|
|
4449
|
-
};
|
|
4426
|
+
// src/db/event-time.ts
|
|
4427
|
+
function daysInMonth(year, month) {
|
|
4428
|
+
const date = new Date(0);
|
|
4429
|
+
date.setUTCFullYear(year, month, 0);
|
|
4430
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
4431
|
+
return date.getUTCDate();
|
|
4432
|
+
}
|
|
4433
|
+
function timestampBaseMs(year, month, day, hour, minute, second) {
|
|
4434
|
+
const date = new Date(0);
|
|
4435
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
4436
|
+
date.setUTCHours(hour, minute, second, 0);
|
|
4437
|
+
return date.getTime();
|
|
4450
4438
|
}
|
|
4451
4439
|
function parseEventTimestamp(value) {
|
|
4452
4440
|
const match = ISO_DATE_TIME_RE.exec(value);
|
|
@@ -4459,23 +4447,28 @@ function parseEventTimestamp(value) {
|
|
|
4459
4447
|
const hour = Number(match[4]);
|
|
4460
4448
|
const minute = Number(match[5]);
|
|
4461
4449
|
const second = Number(match[6]);
|
|
4462
|
-
const
|
|
4463
|
-
const
|
|
4450
|
+
const fraction = match[7] || "";
|
|
4451
|
+
const offset = match[8];
|
|
4452
|
+
const maxDay = month >= 1 && month <= 12 ? daysInMonth(year, month) : 0;
|
|
4464
4453
|
if (month < 1 || month > 12 || day < 1 || day > maxDay || hour > 23 || minute > 59 || second > 59) {
|
|
4465
4454
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
4466
4455
|
}
|
|
4456
|
+
let offsetMinutes = 0;
|
|
4467
4457
|
if (offset !== "Z") {
|
|
4458
|
+
const offsetSign = offset[0] === "-" ? -1 : 1;
|
|
4468
4459
|
const offsetHour = Number(offset.slice(1, 3));
|
|
4469
4460
|
const offsetMinute = Number(offset.slice(4, 6));
|
|
4470
4461
|
if (offsetHour > 23 || offsetMinute > 59) {
|
|
4471
4462
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
4472
4463
|
}
|
|
4464
|
+
offsetMinutes = offsetSign * (offsetHour * 60 + offsetMinute);
|
|
4473
4465
|
}
|
|
4474
|
-
const timestamp =
|
|
4466
|
+
const timestamp = timestampBaseMs(year, month, day, hour, minute, second);
|
|
4475
4467
|
if (!Number.isFinite(timestamp)) {
|
|
4476
4468
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
4477
4469
|
}
|
|
4478
|
-
|
|
4470
|
+
const fractionalNs = BigInt(fraction.padEnd(9, "0") || "0");
|
|
4471
|
+
return BigInt(timestamp) * NS_PER_MS + fractionalNs - BigInt(offsetMinutes) * NS_PER_MINUTE;
|
|
4479
4472
|
}
|
|
4480
4473
|
function assertEventEndsAfterStart(startAt, endAt) {
|
|
4481
4474
|
const start = parseEventTimestamp(startAt);
|
|
@@ -4484,6 +4477,57 @@ function assertEventEndsAfterStart(startAt, endAt) {
|
|
|
4484
4477
|
throw new RangeError("Event end_at must be after start_at");
|
|
4485
4478
|
}
|
|
4486
4479
|
}
|
|
4480
|
+
function parseTimeRange(startAt, endAt) {
|
|
4481
|
+
const start = parseEventTimestamp(startAt);
|
|
4482
|
+
const end = parseEventTimestamp(endAt);
|
|
4483
|
+
if (end <= start) {
|
|
4484
|
+
throw new RangeError("Time range end must be after start");
|
|
4485
|
+
}
|
|
4486
|
+
return { start, end };
|
|
4487
|
+
}
|
|
4488
|
+
function compareEventInstants(a, b) {
|
|
4489
|
+
if (a < b)
|
|
4490
|
+
return -1;
|
|
4491
|
+
if (a > b)
|
|
4492
|
+
return 1;
|
|
4493
|
+
return 0;
|
|
4494
|
+
}
|
|
4495
|
+
function compareEventTimestampStrings(a, b) {
|
|
4496
|
+
return compareEventInstants(parseEventTimestamp(a), parseEventTimestamp(b)) || a.localeCompare(b);
|
|
4497
|
+
}
|
|
4498
|
+
var ISO_DATE_TIME_RE, NS_PER_MS = 1000000n, NS_PER_MINUTE = 60000000000n;
|
|
4499
|
+
var init_event_time = __esm(() => {
|
|
4500
|
+
ISO_DATE_TIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/;
|
|
4501
|
+
});
|
|
4502
|
+
|
|
4503
|
+
// src/db/events.ts
|
|
4504
|
+
function rowToEvent(row) {
|
|
4505
|
+
return {
|
|
4506
|
+
id: row.id,
|
|
4507
|
+
calendar_id: row.calendar_id,
|
|
4508
|
+
org_id: row.org_id,
|
|
4509
|
+
title: row.title,
|
|
4510
|
+
description: row.description,
|
|
4511
|
+
location: row.location,
|
|
4512
|
+
start_at: row.start_at,
|
|
4513
|
+
end_at: row.end_at,
|
|
4514
|
+
all_day: !!row.all_day,
|
|
4515
|
+
timezone: row.timezone,
|
|
4516
|
+
status: row.status,
|
|
4517
|
+
busy_type: row.busy_type,
|
|
4518
|
+
visibility: row.visibility,
|
|
4519
|
+
recurrence_rule: row.recurrence_rule,
|
|
4520
|
+
recurrence_exception_dates: row.recurrence_exception_dates ? JSON.parse(row.recurrence_exception_dates) : null,
|
|
4521
|
+
source_task_id: row.source_task_id,
|
|
4522
|
+
created_by: row.created_by,
|
|
4523
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
4524
|
+
created_at: row.created_at,
|
|
4525
|
+
updated_at: row.updated_at
|
|
4526
|
+
};
|
|
4527
|
+
}
|
|
4528
|
+
function positiveInteger(value) {
|
|
4529
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
4530
|
+
}
|
|
4487
4531
|
function createEvent(input, db) {
|
|
4488
4532
|
db = db || getDatabase();
|
|
4489
4533
|
const id = crypto.randomUUID().slice(0, 8);
|
|
@@ -4501,6 +4545,8 @@ function listEvents(filter = {}, db) {
|
|
|
4501
4545
|
db = db || getDatabase();
|
|
4502
4546
|
const conditions = [];
|
|
4503
4547
|
const params = [];
|
|
4548
|
+
const after = filter.after ? parseEventTimestamp(filter.after) : null;
|
|
4549
|
+
const before = filter.before ? parseEventTimestamp(filter.before) : null;
|
|
4504
4550
|
if (filter.calendar_id) {
|
|
4505
4551
|
conditions.push("calendar_id = ?");
|
|
4506
4552
|
params.push(filter.calendar_id);
|
|
@@ -4513,14 +4559,6 @@ function listEvents(filter = {}, db) {
|
|
|
4513
4559
|
conditions.push("status = ?");
|
|
4514
4560
|
params.push(filter.status);
|
|
4515
4561
|
}
|
|
4516
|
-
if (filter.after) {
|
|
4517
|
-
conditions.push("start_at >= ?");
|
|
4518
|
-
params.push(filter.after);
|
|
4519
|
-
}
|
|
4520
|
-
if (filter.before) {
|
|
4521
|
-
conditions.push("start_at <= ?");
|
|
4522
|
-
params.push(filter.before);
|
|
4523
|
-
}
|
|
4524
4562
|
if (filter.created_by) {
|
|
4525
4563
|
conditions.push("created_by = ?");
|
|
4526
4564
|
params.push(filter.created_by);
|
|
@@ -4530,10 +4568,11 @@ function listEvents(filter = {}, db) {
|
|
|
4530
4568
|
params.push(filter.source_task_id);
|
|
4531
4569
|
}
|
|
4532
4570
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
4533
|
-
const
|
|
4534
|
-
const
|
|
4535
|
-
const
|
|
4536
|
-
|
|
4571
|
+
const rows = db.query(`SELECT * FROM events ${where}`).all(...params);
|
|
4572
|
+
const events = rows.map(rowToEvent).map((event) => ({ event, start: parseEventTimestamp(event.start_at) })).filter(({ start }) => (after === null || start >= after) && (before === null || start <= before)).sort((a, b) => compareEventInstants(a.start, b.start) || a.event.start_at.localeCompare(b.event.start_at)).map(({ event }) => event);
|
|
4573
|
+
const offset = positiveInteger(filter.offset) || 0;
|
|
4574
|
+
const limit = positiveInteger(filter.limit);
|
|
4575
|
+
return limit ? events.slice(offset, offset + limit) : events.slice(offset);
|
|
4537
4576
|
}
|
|
4538
4577
|
function updateEvent(id, input, db) {
|
|
4539
4578
|
db = db || getDatabase();
|
|
@@ -4554,24 +4593,23 @@ function deleteEvent(id, db) {
|
|
|
4554
4593
|
function findConflicts(calendarId, range, excludeEventId, db) {
|
|
4555
4594
|
db = db || getDatabase();
|
|
4556
4595
|
const exclude = excludeEventId ? "AND id != ?" : "";
|
|
4557
|
-
const params = excludeEventId ? [calendarId,
|
|
4558
|
-
const
|
|
4559
|
-
|
|
4596
|
+
const params = excludeEventId ? [calendarId, excludeEventId] : [calendarId];
|
|
4597
|
+
const { start: rangeStart, end: rangeEnd } = parseTimeRange(range.start, range.end);
|
|
4598
|
+
const rows = db.query(`SELECT * FROM events WHERE calendar_id = ? AND status != 'cancelled' ${exclude}`).all(...params);
|
|
4599
|
+
return rows.map(rowToEvent).map((event) => ({ event, start: parseEventTimestamp(event.start_at), end: parseEventTimestamp(event.end_at) })).filter(({ start, end }) => start < rangeEnd && end > rangeStart).sort((a, b) => compareEventInstants(a.start, b.start) || a.event.start_at.localeCompare(b.event.start_at)).map(({ event }) => event);
|
|
4560
4600
|
}
|
|
4561
4601
|
function searchEvents(query, orgId, db) {
|
|
4562
4602
|
db = db || getDatabase();
|
|
4563
4603
|
const rows = db.query(`SELECT e.* FROM events e
|
|
4564
4604
|
INNER JOIN events_fts f ON f.rowid = e.rowid
|
|
4565
4605
|
WHERE events_fts MATCH ?
|
|
4566
|
-
${orgId ? "AND e.org_id = ?" : ""}
|
|
4567
|
-
|
|
4568
|
-
return rows.map(rowToEvent);
|
|
4606
|
+
${orgId ? "AND e.org_id = ?" : ""}`).all(query, ...orgId ? [orgId] : []);
|
|
4607
|
+
return rows.map(rowToEvent).sort((a, b) => compareEventTimestampStrings(a.start_at, b.start_at));
|
|
4569
4608
|
}
|
|
4570
|
-
var ISO_DATE_TIME_RE;
|
|
4571
4609
|
var init_events = __esm(() => {
|
|
4572
4610
|
init_database();
|
|
4611
|
+
init_event_time();
|
|
4573
4612
|
init_types2();
|
|
4574
|
-
ISO_DATE_TIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?(Z|[+-]\d{2}:\d{2})$/;
|
|
4575
4613
|
});
|
|
4576
4614
|
|
|
4577
4615
|
// src/db/attendees.ts
|
|
@@ -4616,6 +4654,7 @@ function updateAttendee(id, input, db) {
|
|
|
4616
4654
|
}
|
|
4617
4655
|
var init_attendees = __esm(() => {
|
|
4618
4656
|
init_database();
|
|
4657
|
+
init_event_time();
|
|
4619
4658
|
init_types2();
|
|
4620
4659
|
});
|
|
4621
4660
|
|
package/dist/server/index.js
CHANGED
|
@@ -21070,30 +21070,18 @@ var init_calendars = __esm(() => {
|
|
|
21070
21070
|
init_types3();
|
|
21071
21071
|
});
|
|
21072
21072
|
|
|
21073
|
-
// src/db/
|
|
21074
|
-
function
|
|
21075
|
-
|
|
21076
|
-
|
|
21077
|
-
|
|
21078
|
-
|
|
21079
|
-
|
|
21080
|
-
|
|
21081
|
-
|
|
21082
|
-
|
|
21083
|
-
|
|
21084
|
-
|
|
21085
|
-
timezone: row.timezone,
|
|
21086
|
-
status: row.status,
|
|
21087
|
-
busy_type: row.busy_type,
|
|
21088
|
-
visibility: row.visibility,
|
|
21089
|
-
recurrence_rule: row.recurrence_rule,
|
|
21090
|
-
recurrence_exception_dates: row.recurrence_exception_dates ? JSON.parse(row.recurrence_exception_dates) : null,
|
|
21091
|
-
source_task_id: row.source_task_id,
|
|
21092
|
-
created_by: row.created_by,
|
|
21093
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
21094
|
-
created_at: row.created_at,
|
|
21095
|
-
updated_at: row.updated_at
|
|
21096
|
-
};
|
|
21073
|
+
// src/db/event-time.ts
|
|
21074
|
+
function daysInMonth(year, month) {
|
|
21075
|
+
const date4 = new Date(0);
|
|
21076
|
+
date4.setUTCFullYear(year, month, 0);
|
|
21077
|
+
date4.setUTCHours(0, 0, 0, 0);
|
|
21078
|
+
return date4.getUTCDate();
|
|
21079
|
+
}
|
|
21080
|
+
function timestampBaseMs(year, month, day, hour, minute, second) {
|
|
21081
|
+
const date4 = new Date(0);
|
|
21082
|
+
date4.setUTCFullYear(year, month - 1, day);
|
|
21083
|
+
date4.setUTCHours(hour, minute, second, 0);
|
|
21084
|
+
return date4.getTime();
|
|
21097
21085
|
}
|
|
21098
21086
|
function parseEventTimestamp(value) {
|
|
21099
21087
|
const match = ISO_DATE_TIME_RE.exec(value);
|
|
@@ -21106,23 +21094,28 @@ function parseEventTimestamp(value) {
|
|
|
21106
21094
|
const hour = Number(match[4]);
|
|
21107
21095
|
const minute = Number(match[5]);
|
|
21108
21096
|
const second = Number(match[6]);
|
|
21109
|
-
const
|
|
21110
|
-
const
|
|
21097
|
+
const fraction = match[7] || "";
|
|
21098
|
+
const offset = match[8];
|
|
21099
|
+
const maxDay = month >= 1 && month <= 12 ? daysInMonth(year, month) : 0;
|
|
21111
21100
|
if (month < 1 || month > 12 || day < 1 || day > maxDay || hour > 23 || minute > 59 || second > 59) {
|
|
21112
21101
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
21113
21102
|
}
|
|
21103
|
+
let offsetMinutes = 0;
|
|
21114
21104
|
if (offset !== "Z") {
|
|
21105
|
+
const offsetSign = offset[0] === "-" ? -1 : 1;
|
|
21115
21106
|
const offsetHour = Number(offset.slice(1, 3));
|
|
21116
21107
|
const offsetMinute = Number(offset.slice(4, 6));
|
|
21117
21108
|
if (offsetHour > 23 || offsetMinute > 59) {
|
|
21118
21109
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
21119
21110
|
}
|
|
21111
|
+
offsetMinutes = offsetSign * (offsetHour * 60 + offsetMinute);
|
|
21120
21112
|
}
|
|
21121
|
-
const timestamp =
|
|
21113
|
+
const timestamp = timestampBaseMs(year, month, day, hour, minute, second);
|
|
21122
21114
|
if (!Number.isFinite(timestamp)) {
|
|
21123
21115
|
throw new RangeError("Event start_at and end_at must be valid ISO 8601 date-time strings");
|
|
21124
21116
|
}
|
|
21125
|
-
|
|
21117
|
+
const fractionalNs = BigInt(fraction.padEnd(9, "0") || "0");
|
|
21118
|
+
return BigInt(timestamp) * NS_PER_MS + fractionalNs - BigInt(offsetMinutes) * NS_PER_MINUTE;
|
|
21126
21119
|
}
|
|
21127
21120
|
function assertEventEndsAfterStart(startAt, endAt) {
|
|
21128
21121
|
const start = parseEventTimestamp(startAt);
|
|
@@ -21131,6 +21124,57 @@ function assertEventEndsAfterStart(startAt, endAt) {
|
|
|
21131
21124
|
throw new RangeError("Event end_at must be after start_at");
|
|
21132
21125
|
}
|
|
21133
21126
|
}
|
|
21127
|
+
function parseTimeRange(startAt, endAt) {
|
|
21128
|
+
const start = parseEventTimestamp(startAt);
|
|
21129
|
+
const end = parseEventTimestamp(endAt);
|
|
21130
|
+
if (end <= start) {
|
|
21131
|
+
throw new RangeError("Time range end must be after start");
|
|
21132
|
+
}
|
|
21133
|
+
return { start, end };
|
|
21134
|
+
}
|
|
21135
|
+
function compareEventInstants(a, b) {
|
|
21136
|
+
if (a < b)
|
|
21137
|
+
return -1;
|
|
21138
|
+
if (a > b)
|
|
21139
|
+
return 1;
|
|
21140
|
+
return 0;
|
|
21141
|
+
}
|
|
21142
|
+
function compareEventTimestampStrings(a, b) {
|
|
21143
|
+
return compareEventInstants(parseEventTimestamp(a), parseEventTimestamp(b)) || a.localeCompare(b);
|
|
21144
|
+
}
|
|
21145
|
+
var ISO_DATE_TIME_RE, NS_PER_MS = 1000000n, NS_PER_MINUTE = 60000000000n;
|
|
21146
|
+
var init_event_time = __esm(() => {
|
|
21147
|
+
ISO_DATE_TIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/;
|
|
21148
|
+
});
|
|
21149
|
+
|
|
21150
|
+
// src/db/events.ts
|
|
21151
|
+
function rowToEvent(row) {
|
|
21152
|
+
return {
|
|
21153
|
+
id: row.id,
|
|
21154
|
+
calendar_id: row.calendar_id,
|
|
21155
|
+
org_id: row.org_id,
|
|
21156
|
+
title: row.title,
|
|
21157
|
+
description: row.description,
|
|
21158
|
+
location: row.location,
|
|
21159
|
+
start_at: row.start_at,
|
|
21160
|
+
end_at: row.end_at,
|
|
21161
|
+
all_day: !!row.all_day,
|
|
21162
|
+
timezone: row.timezone,
|
|
21163
|
+
status: row.status,
|
|
21164
|
+
busy_type: row.busy_type,
|
|
21165
|
+
visibility: row.visibility,
|
|
21166
|
+
recurrence_rule: row.recurrence_rule,
|
|
21167
|
+
recurrence_exception_dates: row.recurrence_exception_dates ? JSON.parse(row.recurrence_exception_dates) : null,
|
|
21168
|
+
source_task_id: row.source_task_id,
|
|
21169
|
+
created_by: row.created_by,
|
|
21170
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
21171
|
+
created_at: row.created_at,
|
|
21172
|
+
updated_at: row.updated_at
|
|
21173
|
+
};
|
|
21174
|
+
}
|
|
21175
|
+
function positiveInteger(value) {
|
|
21176
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
21177
|
+
}
|
|
21134
21178
|
function createEvent(input, db) {
|
|
21135
21179
|
db = db || getDatabase();
|
|
21136
21180
|
const id = crypto.randomUUID().slice(0, 8);
|
|
@@ -21148,6 +21192,8 @@ function listEvents(filter = {}, db) {
|
|
|
21148
21192
|
db = db || getDatabase();
|
|
21149
21193
|
const conditions = [];
|
|
21150
21194
|
const params = [];
|
|
21195
|
+
const after = filter.after ? parseEventTimestamp(filter.after) : null;
|
|
21196
|
+
const before = filter.before ? parseEventTimestamp(filter.before) : null;
|
|
21151
21197
|
if (filter.calendar_id) {
|
|
21152
21198
|
conditions.push("calendar_id = ?");
|
|
21153
21199
|
params.push(filter.calendar_id);
|
|
@@ -21160,14 +21206,6 @@ function listEvents(filter = {}, db) {
|
|
|
21160
21206
|
conditions.push("status = ?");
|
|
21161
21207
|
params.push(filter.status);
|
|
21162
21208
|
}
|
|
21163
|
-
if (filter.after) {
|
|
21164
|
-
conditions.push("start_at >= ?");
|
|
21165
|
-
params.push(filter.after);
|
|
21166
|
-
}
|
|
21167
|
-
if (filter.before) {
|
|
21168
|
-
conditions.push("start_at <= ?");
|
|
21169
|
-
params.push(filter.before);
|
|
21170
|
-
}
|
|
21171
21209
|
if (filter.created_by) {
|
|
21172
21210
|
conditions.push("created_by = ?");
|
|
21173
21211
|
params.push(filter.created_by);
|
|
@@ -21177,10 +21215,11 @@ function listEvents(filter = {}, db) {
|
|
|
21177
21215
|
params.push(filter.source_task_id);
|
|
21178
21216
|
}
|
|
21179
21217
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
21180
|
-
const
|
|
21181
|
-
const
|
|
21182
|
-
const
|
|
21183
|
-
|
|
21218
|
+
const rows = db.query(`SELECT * FROM events ${where}`).all(...params);
|
|
21219
|
+
const events = rows.map(rowToEvent).map((event) => ({ event, start: parseEventTimestamp(event.start_at) })).filter(({ start }) => (after === null || start >= after) && (before === null || start <= before)).sort((a, b) => compareEventInstants(a.start, b.start) || a.event.start_at.localeCompare(b.event.start_at)).map(({ event }) => event);
|
|
21220
|
+
const offset = positiveInteger(filter.offset) || 0;
|
|
21221
|
+
const limit = positiveInteger(filter.limit);
|
|
21222
|
+
return limit ? events.slice(offset, offset + limit) : events.slice(offset);
|
|
21184
21223
|
}
|
|
21185
21224
|
function updateEvent(id, input, db) {
|
|
21186
21225
|
db = db || getDatabase();
|
|
@@ -21201,24 +21240,23 @@ function deleteEvent(id, db) {
|
|
|
21201
21240
|
function findConflicts(calendarId, range, excludeEventId, db) {
|
|
21202
21241
|
db = db || getDatabase();
|
|
21203
21242
|
const exclude = excludeEventId ? "AND id != ?" : "";
|
|
21204
|
-
const params = excludeEventId ? [calendarId,
|
|
21205
|
-
const
|
|
21206
|
-
|
|
21243
|
+
const params = excludeEventId ? [calendarId, excludeEventId] : [calendarId];
|
|
21244
|
+
const { start: rangeStart, end: rangeEnd } = parseTimeRange(range.start, range.end);
|
|
21245
|
+
const rows = db.query(`SELECT * FROM events WHERE calendar_id = ? AND status != 'cancelled' ${exclude}`).all(...params);
|
|
21246
|
+
return rows.map(rowToEvent).map((event) => ({ event, start: parseEventTimestamp(event.start_at), end: parseEventTimestamp(event.end_at) })).filter(({ start, end }) => start < rangeEnd && end > rangeStart).sort((a, b) => compareEventInstants(a.start, b.start) || a.event.start_at.localeCompare(b.event.start_at)).map(({ event }) => event);
|
|
21207
21247
|
}
|
|
21208
21248
|
function searchEvents(query, orgId, db) {
|
|
21209
21249
|
db = db || getDatabase();
|
|
21210
21250
|
const rows = db.query(`SELECT e.* FROM events e
|
|
21211
21251
|
INNER JOIN events_fts f ON f.rowid = e.rowid
|
|
21212
21252
|
WHERE events_fts MATCH ?
|
|
21213
|
-
${orgId ? "AND e.org_id = ?" : ""}
|
|
21214
|
-
|
|
21215
|
-
return rows.map(rowToEvent);
|
|
21253
|
+
${orgId ? "AND e.org_id = ?" : ""}`).all(query, ...orgId ? [orgId] : []);
|
|
21254
|
+
return rows.map(rowToEvent).sort((a, b) => compareEventTimestampStrings(a.start_at, b.start_at));
|
|
21216
21255
|
}
|
|
21217
|
-
var ISO_DATE_TIME_RE;
|
|
21218
21256
|
var init_events = __esm(() => {
|
|
21219
21257
|
init_database();
|
|
21258
|
+
init_event_time();
|
|
21220
21259
|
init_types3();
|
|
21221
|
-
ISO_DATE_TIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?(Z|[+-]\d{2}:\d{2})$/;
|
|
21222
21260
|
});
|
|
21223
21261
|
|
|
21224
21262
|
// src/db/attendees.ts
|
|
@@ -21263,6 +21301,7 @@ function updateAttendee(id, input, db) {
|
|
|
21263
21301
|
}
|
|
21264
21302
|
var init_attendees = __esm(() => {
|
|
21265
21303
|
init_database();
|
|
21304
|
+
init_event_time();
|
|
21266
21305
|
init_types3();
|
|
21267
21306
|
});
|
|
21268
21307
|
|