@timesheet/plugin-google-calendar 1.2.0 → 1.2.2

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.
@@ -194,6 +194,21 @@ async function syncCalendar(context, projectMapping) {
194
194
  if (!syncToken && metadataSyncToken) {
195
195
  syncToken = metadataSyncToken;
196
196
  }
197
+ try {
198
+ return await fetchAndSyncEvents(context, client, projectMapping, calendarId, syncStateKey, syncToken);
199
+ }
200
+ catch (err) {
201
+ // Google returns 400 or 410 when a sync token is invalid/expired — clear it and do a full resync
202
+ const errMsg = String(err);
203
+ if (syncToken && (errMsg.includes('(410)') || errMsg.includes('Invalid sync token'))) {
204
+ context.logger.warn('Sync token expired, performing full resync', { calendarId });
205
+ await context.state.delete(syncStateKey);
206
+ return await fetchAndSyncEvents(context, client, projectMapping, calendarId, syncStateKey, undefined);
207
+ }
208
+ throw err;
209
+ }
210
+ }
211
+ async function fetchAndSyncEvents(context, client, projectMapping, calendarId, syncStateKey, syncToken) {
197
212
  let pageToken;
198
213
  let nextSyncToken;
199
214
  let syncedCount = 0;
@@ -303,10 +318,10 @@ function toTaskDateRange(event) {
303
318
  let endRaw = event.end?.dateTime;
304
319
  // Handle all-day events (date field instead of dateTime)
305
320
  if (!startRaw && event.start?.date) {
306
- startRaw = `${event.start.date}T00:00:00Z`;
321
+ startRaw = `${event.start.date}T00:00:00+00:00`;
307
322
  }
308
323
  if (!endRaw && event.end?.date) {
309
- endRaw = `${event.end.date}T00:00:00Z`;
324
+ endRaw = `${event.end.date}T00:00:00+00:00`;
310
325
  }
311
326
  if (!startRaw || !endRaw) {
312
327
  return null;
@@ -317,8 +332,8 @@ function toTaskDateRange(event) {
317
332
  return null;
318
333
  }
319
334
  return {
320
- startDateTime: start.toISOString(),
321
- endDateTime: end.toISOString()
335
+ startDateTime: toTimesheetDateTime(start),
336
+ endDateTime: toTimesheetDateTime(end)
322
337
  };
323
338
  }
324
339
  function buildGoogleEventPayload(task) {
@@ -478,6 +493,10 @@ async function ensureWatchChannels(context) {
478
493
  }
479
494
  }
480
495
  }
496
+ /** Format a Date to the offset format the Timesheet backend expects: yyyy-MM-dd'T'HH:mm:ss+00:00 */
497
+ function toTimesheetDateTime(date) {
498
+ return date.toISOString().replace(/\.\d{3}Z$/, '+00:00');
499
+ }
481
500
  function readMetadataString(metadata, key) {
482
501
  const value = metadata[key];
483
502
  if (typeof value === 'string' && value.trim().length > 0) {
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "google-calendar-sync",
3
3
  "name": "Google Calendar Sync",
4
- "version": "1.2.0",
4
+ "version": "1.2.2",
5
5
  "description": "Synchronize Timesheet tasks with Google Calendar",
6
6
  "longDescription": "Bidirectional synchronization between Timesheet tasks and Google Calendar events.",
7
7
  "icon": "google-calendar",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timesheet/plugin-google-calendar",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [