@mulmoclaude/core 5.0.0 → 5.1.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.
@@ -1,5 +1,6 @@
1
1
  export { configureGoogleHost, type GoogleLogger } from './host.js';
2
2
  export { isIsoDateTimeWithOffset } from './datetime.js';
3
+ export { isCalendarDateOnly, resolvePartialSpanInput, resolveSpanInput, toEventTimeInput, eventTimeHint, EVENT_TIME_HINT, ALL_DAY_END_HINT, LONE_ALL_DAY_HINT, MIXED_SPAN_HINT, type PartialSpanResult, type PartialSpanTimes, type SpanResult, type SpanTimes, } from './eventSpanInput.js';
3
4
  export { googleConfigDir, googleSecretsDir, googleTokenPath, legacyGoogleTokenPath } from './paths.js';
4
5
  export { clientSecretPresence, findClientSecretPath, loadClientSecret, type ClientSecretPresence, type InstalledClientSecret } from './clientSecret.js';
5
6
  export { deleteGoogleTokens, loadGoogleTokens, mergeGoogleTokens, saveGoogleTokens, type IssuedVia, type StoredGoogleTokens } from './tokenStore.js';
@@ -41,6 +41,104 @@ var isIsoDateTimeWithOffset = (value) => {
41
41
  return isRealCalendarDate(year, month, day) && timeInRange && offsetInRange;
42
42
  };
43
43
  //#endregion
44
+ //#region src/google/eventSpanInput.ts
45
+ var DATE_ONLY_RE$1 = /^\d{4}-\d{2}-\d{2}$/;
46
+ /** Whether `value` is a bare calendar day — the all-day spelling of an event
47
+ * time, and the one shape `isIsoDateTimeWithOffset` is built to refuse.
48
+ *
49
+ * Realness comes from the parser the record lint, the calendar grid and the
50
+ * push already share, so every surface agrees that `2026-02-30` is not a day.
51
+ * The regex runs first because that parser trims, and a value with whitespace
52
+ * around it would validate here and then reach Google verbatim. */
53
+ var isCalendarDateOnly = (value) => DATE_ONLY_RE$1.test(value) && parseIsoDate(value) !== null;
54
+ /** One end of a span, or null when it is neither shape Calendar accepts. */
55
+ var toEventTimeInput = (value) => {
56
+ if (isCalendarDateOnly(value)) return { date: value };
57
+ return isIsoDateTimeWithOffset(value) ? { dateTime: value } : null;
58
+ };
59
+ /** The rule every surface states for a value that is neither shape. Shared so
60
+ * the tool, the remote host and their tests cannot drift apart. */
61
+ var EVENT_TIME_HINT = "must be an ISO 8601 date-time with a timezone offset (e.g. 2026-07-17T09:00:00+09:00), or a date for an all-day event (e.g. 2026-07-17)";
62
+ /** The same rule with the offending key named, for a surface whose error text
63
+ * carries no path of its own. */
64
+ var eventTimeHint = (key) => `${key} ${EVENT_TIME_HINT}`;
65
+ /** All-day `end` is exclusive, so a one-day event ends on the NEXT day. Spelled
66
+ * out because the natural reading of "all day on the 17th" is `end` = the
67
+ * 17th, which Calendar refuses. */
68
+ var ALL_DAY_END_HINT = "an all-day `end` is EXCLUSIVE — it is the day AFTER the last day, so a single day on 2026-07-17 needs end 2026-07-18";
69
+ var MIXED_SPAN_HINT = "start and end must both be date-times with an offset, or both be dates for an all-day event — not one of each";
70
+ /** A lone all-day end cannot be patched: the stored event's kind is unknown
71
+ * here, and giving one end of a timed event a `date` makes an event Calendar
72
+ * rejects — with a message that names neither end. Both together are
73
+ * unambiguous, so that is what the caller is asked for. */
74
+ var LONE_ALL_DAY_HINT = `pass BOTH start and end to move an all-day event — ${ALL_DAY_END_HINT}`;
75
+ var isAllDay = (time) => "date" in time;
76
+ /** Both ends of a create, validated together.
77
+ *
78
+ * Only the all-day ordering is checked: a timed span's ordering depends on the
79
+ * two offsets, which Calendar resolves and reports on clearly, while an all-day
80
+ * `end <= start` comes back as an opaque 400. Lexicographic comparison is exact
81
+ * for `YYYY-MM-DD` — fixed-width, most significant part first. */
82
+ function resolveSpanInput(start, end) {
83
+ const startTime = toEventTimeInput(start);
84
+ if (startTime === null) return {
85
+ ok: false,
86
+ reason: eventTimeHint("start")
87
+ };
88
+ const endTime = toEventTimeInput(end);
89
+ if (endTime === null) return {
90
+ ok: false,
91
+ reason: eventTimeHint("end")
92
+ };
93
+ if (isAllDay(startTime) !== isAllDay(endTime)) return {
94
+ ok: false,
95
+ reason: MIXED_SPAN_HINT
96
+ };
97
+ if (isAllDay(startTime) && isAllDay(endTime) && endTime.date <= startTime.date) return {
98
+ ok: false,
99
+ reason: ALL_DAY_END_HINT
100
+ };
101
+ return {
102
+ ok: true,
103
+ span: {
104
+ start: startTime,
105
+ end: endTime
106
+ }
107
+ };
108
+ }
109
+ /** One end of an EDIT, where the other end is absent. */
110
+ function resolveLoneEnd(key, value) {
111
+ const time = toEventTimeInput(value);
112
+ if (time === null) return {
113
+ ok: false,
114
+ reason: eventTimeHint(key)
115
+ };
116
+ if (isAllDay(time)) return {
117
+ ok: false,
118
+ reason: LONE_ALL_DAY_HINT
119
+ };
120
+ return {
121
+ ok: true,
122
+ times: { [key]: time }
123
+ };
124
+ }
125
+ /** The span of an EDIT, where either end may be absent. */
126
+ function resolvePartialSpanInput(start, end) {
127
+ if (start !== void 0 && end !== void 0) {
128
+ const resolved = resolveSpanInput(start, end);
129
+ return resolved.ok ? {
130
+ ok: true,
131
+ times: resolved.span
132
+ } : resolved;
133
+ }
134
+ if (start !== void 0) return resolveLoneEnd("start", start);
135
+ if (end !== void 0) return resolveLoneEnd("end", end);
136
+ return {
137
+ ok: true,
138
+ times: {}
139
+ };
140
+ }
141
+ //#endregion
44
142
  //#region src/google/paths.ts
45
143
  /** Same directory as every other cross-app per-user file — see
46
144
  * `../global-config/paths.ts`, which owns the spelling. */
@@ -2887,6 +2985,6 @@ async function deleteDriveFile(accessToken, input) {
2887
2985
  await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_FILES_URL}/${encodeURIComponent(input.fileId)}`, { method: "DELETE" });
2888
2986
  }
2889
2987
  //#endregion
2890
- export { CANCELLED_EVENT_STATUS, 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, HTTP_CONFLICT, HTTP_FORBIDDEN, HTTP_PRECONDITION_FAILED, MAX_LIST_RESULTS, PARTIAL_CALENDAR_WINDOW, PROTECTION_UNKNOWN, PUSHABLE_SOURCE_FIELDS, allUnpushed, anySyncedCollectionSurvives, applyPlanFor, assertSafeMimeType, authorizeGoogle, baselineRecord, brokerBaseUrl, brokerExchange, brokerRefresh, brokerStart, buildEventPatch, buildMultipartBody, buildTaskPatch, bySourceField, calendarApiError, calendarBackfillPath, calendarPushStatePath, calendarSyncDueWindowMs, calendarSyncStatePath, canonicalCalendarId, canonicalTaskListId, claimCalendarSyncIfDue, classifyDelete, classifyWrite, clearCalendarLastSyncedAt, clearCalendarShadow, clearCalendarSyncToken, clientSecretPresence, collectCalendarPages, collectionsNeedingBackfill, commitLinkedTokens, completeTask, configureGoogleHost, conflictingFields, createCalendarEvent, createDriveFile, createGoogleAuthFlow, createTask, deleteCalendarEvent, deleteDriveFile, deleteGoogleTokens, deleteTask, fieldText, findClientSecretPath, getCalendarColors, getCalendarEvent, getCalendarMeta, getGoogleAccessToken, googleApiError, googleAuthFlow, googleCalendarSyncTaskDef, googleConfigDir, googleSecretsDir, googleTokenPath, groupByCalendar, groupsNeedingBackfill, heldBack, isCalendarSyncDue, isClientSettableEventId, isDeniedAccessRole, isGoogleApiError, isIsoDateTimeWithOffset, isTextMimeType, isUnpushed, legacyGoogleTokenPath, listCalendarEvents, listCalendars, listDriveFiles, listTaskLists, listTasks, liveLockClock, loadCalendarLastSyncedAt, loadCalendarShadow, loadCalendarSyncToken, loadClientSecret, loadGoogleTokens, locallyChangedFields, locallyDeletedIds, locallyEditedIds, markCalendarBackfilled, mayAdoptExisting, mergeGoogleTokens, mergeIntoExisting, mergeShadow, needsCalendarBackfill, orphanedCalendarId, pickBoundary, planRecord, pullProtectionFor, pullableEvents, pushAndProtect, pushCalendarForCollection, pushCollectionNow, pushableMap, readDriveFile, releaseOrphanedCalendarToken, reportedAccessRole, resolveEventSpan, resumableToken, saveCalendarShadow, saveCalendarSyncToken, saveGoogleTokens, shadowUpdates, stateLockPath, syncCalendarEvents, syncCalendarForCollection, syncCalendarGroup, syncDueCalendarCollections, syncNewCalendarCollections, syncPageParams, toCalendarMeta, toCalendarSummary, toCollectionDateTime, toCollectionRecord, toDriveFileSummary, toEventSummary, toGoogleEventTime, toShadowEvent, toTaskListSummary, toTaskSummary, toolCalendarSyncKey, uncompleteTask, unlinkGoogle, unpushedFor, unsentEditGuard, unsentLocalEdits, updateCalendarEvent, updateTask, waitForAuthCode, windowAdvance, withCalendarLock, withCalendarStateLock, withKeyedLock, withPartialWindowError, zoneSuffixOf };
2988
+ export { ALL_DAY_END_HINT, CANCELLED_EVENT_STATUS, DEFAULT_LIST_MAX_RESULTS, EVENT_TIME_HINT, GOOGLE_AUTH_CANCELLED, GOOGLE_CALENDARLIST_SCOPE, GOOGLE_CALENDAR_SCOPE, GOOGLE_CALENDAR_SYNC_TASK_ID, GOOGLE_DRIVE_FILE_SCOPE, GOOGLE_SCOPES, GOOGLE_TASKS_SCOPE, GoogleApiError, HTTP_CONFLICT, HTTP_FORBIDDEN, HTTP_PRECONDITION_FAILED, LONE_ALL_DAY_HINT, MAX_LIST_RESULTS, MIXED_SPAN_HINT, PARTIAL_CALENDAR_WINDOW, PROTECTION_UNKNOWN, PUSHABLE_SOURCE_FIELDS, allUnpushed, anySyncedCollectionSurvives, applyPlanFor, assertSafeMimeType, authorizeGoogle, baselineRecord, brokerBaseUrl, brokerExchange, brokerRefresh, brokerStart, buildEventPatch, buildMultipartBody, buildTaskPatch, bySourceField, calendarApiError, calendarBackfillPath, calendarPushStatePath, calendarSyncDueWindowMs, calendarSyncStatePath, canonicalCalendarId, canonicalTaskListId, claimCalendarSyncIfDue, classifyDelete, classifyWrite, clearCalendarLastSyncedAt, clearCalendarShadow, clearCalendarSyncToken, clientSecretPresence, collectCalendarPages, collectionsNeedingBackfill, commitLinkedTokens, completeTask, configureGoogleHost, conflictingFields, createCalendarEvent, createDriveFile, createGoogleAuthFlow, createTask, deleteCalendarEvent, deleteDriveFile, deleteGoogleTokens, deleteTask, eventTimeHint, fieldText, findClientSecretPath, getCalendarColors, getCalendarEvent, getCalendarMeta, getGoogleAccessToken, googleApiError, googleAuthFlow, googleCalendarSyncTaskDef, googleConfigDir, googleSecretsDir, googleTokenPath, groupByCalendar, groupsNeedingBackfill, heldBack, isCalendarDateOnly, isCalendarSyncDue, isClientSettableEventId, isDeniedAccessRole, isGoogleApiError, isIsoDateTimeWithOffset, isTextMimeType, isUnpushed, legacyGoogleTokenPath, listCalendarEvents, listCalendars, listDriveFiles, listTaskLists, listTasks, liveLockClock, loadCalendarLastSyncedAt, loadCalendarShadow, loadCalendarSyncToken, loadClientSecret, loadGoogleTokens, locallyChangedFields, locallyDeletedIds, locallyEditedIds, markCalendarBackfilled, mayAdoptExisting, mergeGoogleTokens, mergeIntoExisting, mergeShadow, needsCalendarBackfill, orphanedCalendarId, pickBoundary, planRecord, pullProtectionFor, pullableEvents, pushAndProtect, pushCalendarForCollection, pushCollectionNow, pushableMap, readDriveFile, releaseOrphanedCalendarToken, reportedAccessRole, resolveEventSpan, resolvePartialSpanInput, resolveSpanInput, resumableToken, saveCalendarShadow, saveCalendarSyncToken, saveGoogleTokens, shadowUpdates, stateLockPath, syncCalendarEvents, syncCalendarForCollection, syncCalendarGroup, syncDueCalendarCollections, syncNewCalendarCollections, syncPageParams, toCalendarMeta, toCalendarSummary, toCollectionDateTime, toCollectionRecord, toDriveFileSummary, toEventSummary, toEventTimeInput, toGoogleEventTime, toShadowEvent, toTaskListSummary, toTaskSummary, toolCalendarSyncKey, uncompleteTask, unlinkGoogle, unpushedFor, unsentEditGuard, unsentLocalEdits, updateCalendarEvent, updateTask, waitForAuthCode, windowAdvance, withCalendarLock, withCalendarStateLock, withKeyedLock, withPartialWindowError, zoneSuffixOf };
2891
2989
 
2892
2990
  //# sourceMappingURL=index.js.map