@mulmoclaude/core 1.2.1 → 1.4.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.
- package/README.md +49 -0
- package/assets/helps/google-calendar-collection.md +13 -2
- package/assets/helps/google.md +94 -0
- package/assets/helps/index.md +1 -0
- package/dist/collection/registry/server/index.cjs +3 -2
- package/dist/collection/registry/server/index.cjs.map +1 -1
- package/dist/collection/registry/server/index.js +3 -2
- package/dist/collection/registry/server/index.js.map +1 -1
- package/dist/collection/server/index.cjs +1 -1
- package/dist/collection/server/index.js +1 -1
- package/dist/collection-watchers/index.cjs +1 -1
- package/dist/collection-watchers/index.js +1 -1
- package/dist/feeds/server/index.cjs +2 -2
- package/dist/feeds/server/index.js +2 -2
- package/dist/files/index.cjs +3 -0
- package/dist/files/index.cjs.map +1 -1
- package/dist/files/index.d.ts +1 -0
- package/dist/files/index.js +2 -1
- package/dist/files/index.js.map +1 -1
- package/dist/files/relPath.d.ts +23 -0
- package/dist/google/calendar.d.ts +28 -0
- package/dist/google/collectionSync.d.ts +88 -4
- package/dist/google/index.cjs +235 -17
- package/dist/google/index.cjs.map +1 -1
- package/dist/google/index.d.ts +3 -3
- package/dist/google/index.js +223 -18
- package/dist/google/index.js.map +1 -1
- package/dist/google/tasks.d.ts +21 -0
- package/dist/relPath-CgGi4-Nv.cjs +46 -0
- package/dist/relPath-CgGi4-Nv.cjs.map +1 -0
- package/dist/relPath-DW8MC8VO.js +33 -0
- package/dist/relPath-DW8MC8VO.js.map +1 -0
- package/dist/{server-BZJCw2HU.js → server-D_6FLygK.js} +6 -2
- package/dist/server-D_6FLygK.js.map +1 -0
- package/dist/{server-w5ZZRLju.cjs → server-gEOeV_Nv.cjs} +6 -2
- package/dist/server-gEOeV_Nv.cjs.map +1 -0
- package/package.json +10 -1
- package/dist/server-BZJCw2HU.js.map +0 -1
- package/dist/server-w5ZZRLju.cjs.map +0 -1
package/dist/google/index.d.ts
CHANGED
|
@@ -7,9 +7,9 @@ export { brokerBaseUrl, brokerExchange, brokerRefresh, brokerStart, type BrokerS
|
|
|
7
7
|
export { authorizeGoogle, commitLinkedTokens, getGoogleAccessToken, unlinkGoogle, waitForAuthCode, GOOGLE_AUTH_CANCELLED, GOOGLE_CALENDAR_SCOPE, GOOGLE_CALENDARLIST_SCOPE, GOOGLE_TASKS_SCOPE, GOOGLE_DRIVE_FILE_SCOPE, GOOGLE_SCOPES, type AuthorizeGoogleOptions, type RevokeFetch, } from './auth.js';
|
|
8
8
|
export { createGoogleAuthFlow, googleAuthFlow, type GoogleAuthFlow, type GoogleAuthFlowStatus } from './authFlow.js';
|
|
9
9
|
export { googleApiError, isGoogleApiError, GoogleApiError, DEFAULT_LIST_MAX_RESULTS, MAX_LIST_RESULTS } from './apiClient.js';
|
|
10
|
-
export { calendarApiError, collectCalendarPages, createCalendarEvent, getCalendarColors, canonicalCalendarId, listCalendarEvents, listCalendars, syncCalendarEvents, toCalendarSummary, toEventSummary, type CalendarColorEntry, type CalendarColors, type CalendarEventInput, type CalendarEventSummary, type CalendarListPage, type CalendarSummary, type CalendarSyncResult, type ListEventsInput, type SyncEventsInput, } from './calendar.js';
|
|
10
|
+
export { buildEventPatch, calendarApiError, collectCalendarPages, createCalendarEvent, deleteCalendarEvent, getCalendarColors, canonicalCalendarId, listCalendarEvents, listCalendars, syncCalendarEvents, toCalendarSummary, toEventSummary, updateCalendarEvent, type CalendarColorEntry, type CalendarColors, type CalendarEventInput, type CalendarEventSummary, type CalendarListPage, type CalendarSummary, type CalendarSyncResult, type DeleteCalendarEventInput, type ListEventsInput, type SyncEventsInput, type UpdateCalendarEventInput, } from './calendar.js';
|
|
11
11
|
export { calendarSyncStatePath, clearCalendarSyncToken, loadCalendarSyncToken, saveCalendarSyncToken } from './calendarSyncStore.js';
|
|
12
12
|
export { toCollectionDateTime } from './collectionDateTime.js';
|
|
13
|
-
export { googleCalendarSyncTaskDef, classifyDelete, classifyWrite, groupByCalendar, syncCalendarGroup, syncDueCalendarCollections, toCollectionRecord, GOOGLE_CALENDAR_SYNC_TASK_ID, type CalendarCollectionSyncResult, } from './collectionSync.js';
|
|
14
|
-
export { completeTask, createTask, deleteTask, listTaskLists, listTasks, toTaskListSummary, toTaskSummary, type CompleteTaskInput, type CreateTaskInput, type ListTasksInput, type TaskListSummary, type TaskSummary, } from './tasks.js';
|
|
13
|
+
export { googleCalendarSyncTaskDef, classifyDelete, classifyWrite, anySyncedCollectionSurvives, groupByCalendar, orphanedCalendarId, releaseOrphanedCalendarToken, syncCalendarForCollection, syncCalendarGroup, syncDueCalendarCollections, syncNewCalendarCollections, toCollectionRecord, unsyncedGroups, withKeyedLock, GOOGLE_CALENDAR_SYNC_TASK_ID, type CalendarCollectionSyncResult, type CalendarDeclaring, type ManualCalendarSyncDeps, type ManualCalendarSyncOutcome, } from './collectionSync.js';
|
|
14
|
+
export { buildTaskPatch, canonicalTaskListId, completeTask, createTask, deleteTask, listTaskLists, listTasks, toTaskListSummary, toTaskSummary, updateTask, type CompleteTaskInput, type CreateTaskInput, type ListTasksInput, type TaskListSummary, type TaskSummary, type UpdateTaskInput, } from './tasks.js';
|
|
15
15
|
export { assertSafeMimeType, buildMultipartBody, createDriveFile, deleteDriveFile, isTextMimeType, listDriveFiles, pickBoundary, readDriveFile, toDriveFileSummary, type CreateDriveFileInput, type DriveFileSummary, type ListDriveFilesInput, type ReadDriveFileInput, } from './driveFile.js';
|
package/dist/google/index.js
CHANGED
|
@@ -651,6 +651,34 @@ async function createCalendarEvent(accessToken, input) {
|
|
|
651
651
|
body: JSON.stringify(body)
|
|
652
652
|
}));
|
|
653
653
|
}
|
|
654
|
+
var eventUrl = (calendarId, eventId) => `${eventsUrl(calendarId)}/${encodeURIComponent(eventId)}`;
|
|
655
|
+
/** PATCH body for an event edit — only the fields the caller actually supplied.
|
|
656
|
+
*
|
|
657
|
+
* `undefined` means "leave as is" and `""` means "clear it", so the two cannot
|
|
658
|
+
* be collapsed: dropping `description: ""` would silently ignore a request to
|
|
659
|
+
* empty the body text. Pure so the distinction is testable without network. */
|
|
660
|
+
var buildEventPatch = (input) => ({
|
|
661
|
+
...input.summary !== void 0 ? { summary: input.summary } : {},
|
|
662
|
+
...input.description !== void 0 ? { description: input.description } : {},
|
|
663
|
+
...input.startDateTime !== void 0 ? { start: { dateTime: input.startDateTime } } : {},
|
|
664
|
+
...input.endDateTime !== void 0 ? { end: { dateTime: input.endDateTime } } : {},
|
|
665
|
+
...input.colorId ? { colorId: input.colorId } : {}
|
|
666
|
+
});
|
|
667
|
+
/** Edit an existing event in place. PATCH, not PUT — a PUT would need the whole
|
|
668
|
+
* event and would drop every field the caller never read (attendees,
|
|
669
|
+
* reminders, recurrence). */
|
|
670
|
+
async function updateCalendarEvent(accessToken, input) {
|
|
671
|
+
return toEventSummary(await googleRequest(CALENDAR_API_LABEL, accessToken, eventUrl(input.calendarId, input.eventId), {
|
|
672
|
+
method: "PATCH",
|
|
673
|
+
body: JSON.stringify(buildEventPatch(input))
|
|
674
|
+
}));
|
|
675
|
+
}
|
|
676
|
+
/** Remove an event. Google answers 204 with no body, so there is nothing to
|
|
677
|
+
* return; a second delete of the same id answers 410, which surfaces as a
|
|
678
|
+
* GoogleApiError rather than being swallowed. */
|
|
679
|
+
async function deleteCalendarEvent(accessToken, input) {
|
|
680
|
+
await googleRequest(CALENDAR_API_LABEL, accessToken, eventUrl(input.calendarId, input.eventId), { method: "DELETE" });
|
|
681
|
+
}
|
|
654
682
|
async function listCalendarEvents(accessToken, input = {}) {
|
|
655
683
|
const params = new URLSearchParams({
|
|
656
684
|
timeMin: input.timeMin ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -879,6 +907,26 @@ async function restartFullSync(accessToken, calendarId, workspaceRoot) {
|
|
|
879
907
|
await clearCalendarSyncToken(calendarId, workspaceRoot);
|
|
880
908
|
return await syncCalendarEvents(accessToken, { calendarId });
|
|
881
909
|
}
|
|
910
|
+
/** Serialise `run` against whatever is already running for `key`.
|
|
911
|
+
*
|
|
912
|
+
* `locks` is passed in so the queuing rule is testable without module state;
|
|
913
|
+
* the key is dropped once nothing is queued behind it, so the map cannot grow
|
|
914
|
+
* an entry per calendar forever. A failed predecessor still releases the
|
|
915
|
+
* queue — `then(run, run)`. */
|
|
916
|
+
async function withKeyedLock(locks, key, run) {
|
|
917
|
+
const result = (locks.get(key) ?? Promise.resolve()).then(run, run);
|
|
918
|
+
const tail = result.then(() => void 0, () => void 0);
|
|
919
|
+
locks.set(key, tail);
|
|
920
|
+
try {
|
|
921
|
+
return await result;
|
|
922
|
+
} finally {
|
|
923
|
+
if (locks.get(key) === tail) locks.delete(key);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
/** In-flight sync per canonical calendar id. Module state on purpose: the
|
|
927
|
+
* scheduler, the create trigger and the Refresh button are three doors into
|
|
928
|
+
* the same calendar (CodeRabbit review #2566). */
|
|
929
|
+
var calendarLocks = /* @__PURE__ */ new Map();
|
|
882
930
|
/** Sync ONE calendar and fan its events out to every collection bound to it.
|
|
883
931
|
*
|
|
884
932
|
* The fan-out is not an optimisation, it is correctness: the sync token is
|
|
@@ -886,8 +934,18 @@ async function restartFullSync(accessToken, calendarId, workspaceRoot) {
|
|
|
886
934
|
* first collection advance the shared token and leave every later collection
|
|
887
935
|
* on the same calendar reading an already-consumed window — silently missing
|
|
888
936
|
* those events forever. Fetch once, apply to all, then advance the token.
|
|
889
|
-
* (Codex + CodeRabbit review on #2184.)
|
|
937
|
+
* (Codex + CodeRabbit review on #2184.)
|
|
938
|
+
*
|
|
939
|
+
* Queued per calendar for the same reason the fan-out exists: two passes over
|
|
940
|
+
* one calendar (a Refresh click landing during the scheduled run) would each
|
|
941
|
+
* load the SAME stored token and walk the same window. That is idempotent —
|
|
942
|
+
* writes are upserts by event id — but it is a wasted full walk. Queued, the
|
|
943
|
+
* second pass resumes from the token the first just stored and fetches only
|
|
944
|
+
* what is genuinely new. */
|
|
890
945
|
async function syncCalendarGroup(calendarId, collections, workspaceRoot) {
|
|
946
|
+
return await withKeyedLock(calendarLocks, canonicalCalendarId(calendarId), () => syncCalendarGroupNow(calendarId, collections, workspaceRoot));
|
|
947
|
+
}
|
|
948
|
+
async function syncCalendarGroupNow(calendarId, collections, workspaceRoot) {
|
|
891
949
|
const accessToken = await getGoogleAccessToken();
|
|
892
950
|
const first = await syncCalendarEvents(accessToken, {
|
|
893
951
|
calendarId,
|
|
@@ -902,13 +960,47 @@ async function syncCalendarGroup(calendarId, collections, workspaceRoot) {
|
|
|
902
960
|
unwritable
|
|
903
961
|
});
|
|
904
962
|
const failed = results.flatMap((entry) => entry.errors);
|
|
905
|
-
if (
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
963
|
+
if (failed.length > 0) {
|
|
964
|
+
log.warn("google", "holding back calendar sync token after failed writes", {
|
|
965
|
+
calendarId,
|
|
966
|
+
failed: failed.length
|
|
967
|
+
});
|
|
968
|
+
return results;
|
|
969
|
+
}
|
|
970
|
+
if (result.nextSyncToken) await advanceToken(calendarId, result.nextSyncToken, collections, workspaceRoot);
|
|
910
971
|
return results;
|
|
911
972
|
}
|
|
973
|
+
/** Save the window's token unless every collection that consumed it was deleted
|
|
974
|
+
* while the sync was in flight.
|
|
975
|
+
*
|
|
976
|
+
* The sync opens with a `discoverCollections()` snapshot, so a delete landing
|
|
977
|
+
* mid-run has already cleared this calendar's token by the time we get here
|
|
978
|
+
* (`releaseOrphanedCalendarToken`). Saving anyway would resurrect exactly the
|
|
979
|
+
* orphan the delete removed, and the next collection on this calendar would
|
|
980
|
+
* resume from a token describing records it never received (#2428).
|
|
981
|
+
*
|
|
982
|
+
* One survivor is enough: it still needs the incremental position. */
|
|
983
|
+
async function advanceToken(calendarId, nextSyncToken, collections, workspaceRoot) {
|
|
984
|
+
if (!await anySyncedCollectionSurvives(collections)) {
|
|
985
|
+
log.info("google", "not advancing the sync token — every collection on this calendar was deleted mid-sync", { calendarId });
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
await saveCalendarSyncToken(calendarId, nextSyncToken, workspaceRoot);
|
|
989
|
+
}
|
|
990
|
+
async function pathExists(absPath) {
|
|
991
|
+
try {
|
|
992
|
+
await stat(absPath);
|
|
993
|
+
return true;
|
|
994
|
+
} catch {
|
|
995
|
+
return false;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
/** Liveness of the collections a sync just wrote to, checked against the skill
|
|
999
|
+
* dir `deleteCollection` removes. `exists` is injected so the rule is testable
|
|
1000
|
+
* without a filesystem. An empty group has no survivor by definition. */
|
|
1001
|
+
async function anySyncedCollectionSurvives(collections, exists = pathExists) {
|
|
1002
|
+
return (await Promise.all(collections.map((collection) => exists(collection.skillDir)))).some(Boolean);
|
|
1003
|
+
}
|
|
912
1004
|
async function applyEventsToCollection(collection, events, workspaceRoot) {
|
|
913
1005
|
const outcomes = [];
|
|
914
1006
|
for (const event of events) outcomes.push(await applyEvent(collection, event, workspaceRoot));
|
|
@@ -941,18 +1033,62 @@ function groupByCalendar(collections) {
|
|
|
941
1033
|
}
|
|
942
1034
|
return groups;
|
|
943
1035
|
}
|
|
944
|
-
/**
|
|
945
|
-
*
|
|
946
|
-
*
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1036
|
+
/** The canonical calendar whose sync token nothing needs any more, or null.
|
|
1037
|
+
*
|
|
1038
|
+
* Sync tokens are keyed by calendar, NOT by collection, so a deleted
|
|
1039
|
+
* collection's token outlives it. Recreating a collection on the same calendar
|
|
1040
|
+
* then resumes from that token and receives only the delta — the new
|
|
1041
|
+
* collection never gets the history (#2428).
|
|
1042
|
+
*
|
|
1043
|
+
* Returns null while ANY remaining collection still reads that calendar:
|
|
1044
|
+
* clearing a live calendar's token costs a full re-walk on the next sync. The
|
|
1045
|
+
* comparison is on the CANONICAL id for the same reason `groupByCalendar` is —
|
|
1046
|
+
* an omitted `calendarId` and an explicit `"primary"` are one calendar. */
|
|
1047
|
+
function orphanedCalendarId(deleted, remaining) {
|
|
1048
|
+
if (!deleted.googleCalendar) return null;
|
|
1049
|
+
const key = canonicalCalendarId(deleted.googleCalendar.calendarId);
|
|
1050
|
+
return remaining.some((other) => other.googleCalendar && canonicalCalendarId(other.googleCalendar.calendarId) === key) ? null : key;
|
|
1051
|
+
}
|
|
1052
|
+
/** Drop the sync token of a just-deleted collection's calendar, unless another
|
|
1053
|
+
* collection still reads it. Call AFTER the delete lands — the check reads the
|
|
1054
|
+
* collections that survive it.
|
|
1055
|
+
*
|
|
1056
|
+
* Returns the cleared calendar id, or null when nothing was cleared. Never
|
|
1057
|
+
* throws: a failed cleanup must not fail the delete it follows. */
|
|
1058
|
+
async function releaseOrphanedCalendarToken(deleted, workspaceRoot) {
|
|
1059
|
+
try {
|
|
1060
|
+
if (!deleted.googleCalendar) return null;
|
|
1061
|
+
const orphaned = orphanedCalendarId(deleted, (await discoverCollections({ workspaceRoot })).map((collection) => collection.schema));
|
|
1062
|
+
if (orphaned === null) return null;
|
|
1063
|
+
await clearCalendarSyncToken(orphaned, workspaceRoot);
|
|
1064
|
+
log.info("google", "cleared the sync token of a calendar no collection reads any more", { calendarId: orphaned });
|
|
1065
|
+
return orphaned;
|
|
1066
|
+
} catch (error) {
|
|
1067
|
+
log.warn("google", "could not release the deleted collection's calendar sync token", { error: String(error) });
|
|
1068
|
+
return null;
|
|
953
1069
|
}
|
|
1070
|
+
}
|
|
1071
|
+
/** Every declaring collection, grouped by the calendar it reads. */
|
|
1072
|
+
async function declaringGroups(workspaceRoot) {
|
|
1073
|
+
return groupByCalendar((await discoverCollections({ workspaceRoot })).filter((collection) => collection.schema.googleCalendar));
|
|
1074
|
+
}
|
|
1075
|
+
/** Whether a background sync of these groups may run at all.
|
|
1076
|
+
*
|
|
1077
|
+
* Authoring the collection before linking the account is an expected state,
|
|
1078
|
+
* not a failure. Checking once here keeps it a quiet skip instead of an
|
|
1079
|
+
* access-token throw per calendar, every hour, until the user links (#2188).
|
|
1080
|
+
* A user-triggered sync answers differently — it says so out loud. */
|
|
1081
|
+
async function backgroundSyncAllowed(groups) {
|
|
1082
|
+
if (groups.size === 0) return false;
|
|
1083
|
+
if (await isGoogleLinked()) return true;
|
|
1084
|
+
log.info("google", "skipping calendar sync — no Google account linked on this host", { calendars: groups.size });
|
|
1085
|
+
return false;
|
|
1086
|
+
}
|
|
1087
|
+
/** Run each group, isolating failures per calendar — one unreachable calendar
|
|
1088
|
+
* (or a revoked grant) must not stop the others. */
|
|
1089
|
+
async function runCalendarGroups(groups, workspaceRoot) {
|
|
954
1090
|
const results = [];
|
|
955
|
-
for (const [calendarId, collections] of
|
|
1091
|
+
for (const [calendarId, collections] of groups) try {
|
|
956
1092
|
results.push(...await syncCalendarGroup(calendarId, collections, workspaceRoot));
|
|
957
1093
|
} catch (error) {
|
|
958
1094
|
log.warn("google", "calendar sync failed", {
|
|
@@ -969,6 +1105,55 @@ async function syncDueCalendarCollections(workspaceRoot) {
|
|
|
969
1105
|
}
|
|
970
1106
|
return results;
|
|
971
1107
|
}
|
|
1108
|
+
/** Sync every collection that declares `googleCalendar`. */
|
|
1109
|
+
async function syncDueCalendarCollections(workspaceRoot) {
|
|
1110
|
+
const groups = await declaringGroups(workspaceRoot);
|
|
1111
|
+
return await backgroundSyncAllowed(groups) ? await runCalendarGroups(groups, workspaceRoot) : [];
|
|
1112
|
+
}
|
|
1113
|
+
/** The groups whose calendar has never synced. A missing token IS the "created
|
|
1114
|
+
* since the last sync" signal — nothing else distinguishes a new collection
|
|
1115
|
+
* from an edited one on the write path this feeds (#2427).
|
|
1116
|
+
*
|
|
1117
|
+
* Self-silencing by construction: the first sync stores a token, so a calendar
|
|
1118
|
+
* matches at most once. `loadToken` is injected so the rule is testable without
|
|
1119
|
+
* a workspace on disk. */
|
|
1120
|
+
async function unsyncedGroups(groups, loadToken) {
|
|
1121
|
+
const checked = await Promise.all([...groups].map(async (entry) => await loadToken(entry[0]) === null ? entry : null));
|
|
1122
|
+
return new Map(checked.filter((entry) => entry !== null));
|
|
1123
|
+
}
|
|
1124
|
+
/** Sync only the calendars that have never synced — the first sync for a
|
|
1125
|
+
* just-created collection, which otherwise stays empty until the hourly
|
|
1126
|
+
* scheduler run (#2427). Cheap and safe to call on every config write. */
|
|
1127
|
+
async function syncNewCalendarCollections(workspaceRoot) {
|
|
1128
|
+
const pending = await unsyncedGroups(await declaringGroups(workspaceRoot), (calendarId) => loadCalendarSyncToken(calendarId, workspaceRoot));
|
|
1129
|
+
if (!await backgroundSyncAllowed(pending)) return [];
|
|
1130
|
+
log.info("google", "running the first sync for newly declared calendars", { calendars: [...pending.keys()] });
|
|
1131
|
+
return await runCalendarGroups(pending, workspaceRoot);
|
|
1132
|
+
}
|
|
1133
|
+
var liveManualSyncDeps = {
|
|
1134
|
+
loadGroups: declaringGroups,
|
|
1135
|
+
isLinked: isGoogleLinked,
|
|
1136
|
+
runGroups: runCalendarGroups
|
|
1137
|
+
};
|
|
1138
|
+
/** Sync the calendar ONE collection reads, on demand (the Refresh button).
|
|
1139
|
+
*
|
|
1140
|
+
* Deliberately syncs the whole group, not just `slug`: the sync token is keyed
|
|
1141
|
+
* by calendar, so consuming a window for one collection would leave the others
|
|
1142
|
+
* on that calendar reading an already-consumed one. Returns every result of the
|
|
1143
|
+
* group so the caller can report the requested slug's own counts.
|
|
1144
|
+
*
|
|
1145
|
+
* "Does this collection sync at all" is answered BEFORE "is Google linked":
|
|
1146
|
+
* telling someone to link their account for a collection that never declared a
|
|
1147
|
+
* calendar sends them fixing the wrong thing. */
|
|
1148
|
+
async function syncCalendarForCollection(slug, workspaceRoot, deps = liveManualSyncDeps) {
|
|
1149
|
+
const owning = [...await deps.loadGroups(workspaceRoot)].filter(([, collections]) => collections.some((collection) => collection.slug === slug));
|
|
1150
|
+
if (owning.length === 0) return { kind: "not-a-calendar" };
|
|
1151
|
+
if (!await deps.isLinked()) return { kind: "not-linked" };
|
|
1152
|
+
return {
|
|
1153
|
+
kind: "synced",
|
|
1154
|
+
results: await deps.runGroups(new Map(owning), workspaceRoot)
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
972
1157
|
/** Scheduler registration, shaped like `feedRefreshTaskDef` so hosts wire it
|
|
973
1158
|
* with a single line. */
|
|
974
1159
|
function googleCalendarSyncTaskDef(opts) {
|
|
@@ -1008,7 +1193,12 @@ var toTaskSummary = (value) => {
|
|
|
1008
1193
|
notes: stringField(record, "notes")
|
|
1009
1194
|
};
|
|
1010
1195
|
};
|
|
1011
|
-
|
|
1196
|
+
/** Resolve a declared taskListId to the one the API addresses. `||` (not `??`)
|
|
1197
|
+
* so a blank string also falls back instead of building a malformed
|
|
1198
|
+
* `/lists//tasks` URL — the same rule, and the same reason, as
|
|
1199
|
+
* `canonicalCalendarId`. */
|
|
1200
|
+
var canonicalTaskListId = (taskListId) => taskListId?.trim() || DEFAULT_TASK_LIST_ID;
|
|
1201
|
+
var tasksUrl = (taskListId, suffix = "") => `${TASKS_BASE_URL}/lists/${encodeURIComponent(canonicalTaskListId(taskListId))}/tasks${suffix}`;
|
|
1012
1202
|
async function listTaskLists(accessToken) {
|
|
1013
1203
|
return itemsOf(await googleRequest(TASKS_API_LABEL, accessToken, `${TASKS_BASE_URL}/users/@me/lists?maxResults=${MAX_TASK_LISTS}`)).map(toTaskListSummary);
|
|
1014
1204
|
}
|
|
@@ -1030,6 +1220,21 @@ async function createTask(accessToken, input) {
|
|
|
1030
1220
|
body: JSON.stringify(body)
|
|
1031
1221
|
}));
|
|
1032
1222
|
}
|
|
1223
|
+
/** PATCH body for a task edit — only the fields the caller supplied. As with
|
|
1224
|
+
* events, `undefined` means "leave as is" and `""` means "clear it", so the
|
|
1225
|
+
* two must stay distinct. `status` is deliberately absent: `completeTask`
|
|
1226
|
+
* owns that transition, and two ways to set it would drift apart. */
|
|
1227
|
+
var buildTaskPatch = (input) => ({
|
|
1228
|
+
...input.title !== void 0 ? { title: input.title } : {},
|
|
1229
|
+
...input.notes !== void 0 ? { notes: input.notes } : {},
|
|
1230
|
+
...input.due !== void 0 ? { due: input.due } : {}
|
|
1231
|
+
});
|
|
1232
|
+
async function updateTask(accessToken, input) {
|
|
1233
|
+
return toTaskSummary(await googleRequest(TASKS_API_LABEL, accessToken, tasksUrl(input.taskListId, `/${encodeURIComponent(input.taskId)}`), {
|
|
1234
|
+
method: "PATCH",
|
|
1235
|
+
body: JSON.stringify(buildTaskPatch(input))
|
|
1236
|
+
}));
|
|
1237
|
+
}
|
|
1033
1238
|
async function completeTask(accessToken, input) {
|
|
1034
1239
|
return toTaskSummary(await googleRequest(TASKS_API_LABEL, accessToken, tasksUrl(input.taskListId, `/${encodeURIComponent(input.taskId)}`), {
|
|
1035
1240
|
method: "PATCH",
|
|
@@ -1133,6 +1338,6 @@ async function deleteDriveFile(accessToken, input) {
|
|
|
1133
1338
|
await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_FILES_URL}/${encodeURIComponent(input.fileId)}`, { method: "DELETE" });
|
|
1134
1339
|
}
|
|
1135
1340
|
//#endregion
|
|
1136
|
-
export { DEFAULT_LIST_MAX_RESULTS, GOOGLE_AUTH_CANCELLED, GOOGLE_CALENDARLIST_SCOPE, GOOGLE_CALENDAR_SCOPE, GOOGLE_CALENDAR_SYNC_TASK_ID, GOOGLE_DRIVE_FILE_SCOPE, GOOGLE_SCOPES, GOOGLE_TASKS_SCOPE, GoogleApiError, MAX_LIST_RESULTS, assertSafeMimeType, authorizeGoogle, brokerBaseUrl, brokerExchange, brokerRefresh, brokerStart, buildMultipartBody, calendarApiError, calendarSyncStatePath, canonicalCalendarId, classifyDelete, classifyWrite, clearCalendarSyncToken, clientSecretPresence, collectCalendarPages, commitLinkedTokens, completeTask, configureGoogleHost, createCalendarEvent, createDriveFile, createGoogleAuthFlow, createTask, deleteDriveFile, deleteGoogleTokens, deleteTask, findClientSecretPath, getCalendarColors, getGoogleAccessToken, googleApiError, googleAuthFlow, googleCalendarSyncTaskDef, googleConfigDir, googleSecretsDir, googleTokenPath, groupByCalendar, isGoogleApiError, isIsoDateTimeWithOffset, isTextMimeType, legacyGoogleTokenPath, listCalendarEvents, listCalendars, listDriveFiles, listTaskLists, listTasks, loadCalendarSyncToken, loadClientSecret, loadGoogleTokens, mergeGoogleTokens, pickBoundary, readDriveFile, saveCalendarSyncToken, saveGoogleTokens, syncCalendarEvents, syncCalendarGroup, syncDueCalendarCollections, toCalendarSummary, toCollectionDateTime, toCollectionRecord, toDriveFileSummary, toEventSummary, toTaskListSummary, toTaskSummary, unlinkGoogle, waitForAuthCode };
|
|
1341
|
+
export { DEFAULT_LIST_MAX_RESULTS, GOOGLE_AUTH_CANCELLED, GOOGLE_CALENDARLIST_SCOPE, GOOGLE_CALENDAR_SCOPE, GOOGLE_CALENDAR_SYNC_TASK_ID, GOOGLE_DRIVE_FILE_SCOPE, GOOGLE_SCOPES, GOOGLE_TASKS_SCOPE, GoogleApiError, MAX_LIST_RESULTS, anySyncedCollectionSurvives, assertSafeMimeType, authorizeGoogle, brokerBaseUrl, brokerExchange, brokerRefresh, brokerStart, buildEventPatch, buildMultipartBody, buildTaskPatch, calendarApiError, calendarSyncStatePath, canonicalCalendarId, canonicalTaskListId, classifyDelete, classifyWrite, clearCalendarSyncToken, clientSecretPresence, collectCalendarPages, commitLinkedTokens, completeTask, configureGoogleHost, createCalendarEvent, createDriveFile, createGoogleAuthFlow, createTask, deleteCalendarEvent, deleteDriveFile, deleteGoogleTokens, deleteTask, findClientSecretPath, getCalendarColors, getGoogleAccessToken, googleApiError, googleAuthFlow, googleCalendarSyncTaskDef, googleConfigDir, googleSecretsDir, googleTokenPath, groupByCalendar, isGoogleApiError, isIsoDateTimeWithOffset, isTextMimeType, legacyGoogleTokenPath, listCalendarEvents, listCalendars, listDriveFiles, listTaskLists, listTasks, loadCalendarSyncToken, loadClientSecret, loadGoogleTokens, mergeGoogleTokens, orphanedCalendarId, pickBoundary, readDriveFile, releaseOrphanedCalendarToken, saveCalendarSyncToken, saveGoogleTokens, syncCalendarEvents, syncCalendarForCollection, syncCalendarGroup, syncDueCalendarCollections, syncNewCalendarCollections, toCalendarSummary, toCollectionDateTime, toCollectionRecord, toDriveFileSummary, toEventSummary, toTaskListSummary, toTaskSummary, unlinkGoogle, unsyncedGroups, updateCalendarEvent, updateTask, waitForAuthCode, withKeyedLock };
|
|
1137
1342
|
|
|
1138
1343
|
//# sourceMappingURL=index.js.map
|