refactor(calendar): provider-neutral adapter interface and error taxonomy (#1391) - #1407
Draft
h4yfans wants to merge 2 commits into
Draft
refactor(calendar): provider-neutral adapter interface and error taxonomy (#1391)#1407h4yfans wants to merge 2 commits into
h4yfans wants to merge 2 commits into
Conversation
…nomy (#1391) The calendar layer already had an abstraction — `GoogleCalendarClient` — but Google concepts had leaked through it: `watchCalendar`/`stopChannel` are literally Google's push-channel API, `syncCursor` carried syncToken semantics, and error handling was a `status === 410` sniff on Google's own wire shape. Introduce `calendar/provider/`: - `adapter.ts` — `CalendarProviderAdapter` plus `ProviderCapabilities` (`supportsWrite`, `supportsCreateCalendar`, `supportsPush`, `supportsMultiAccount`, `incrementalMode`, `authFlow`). Optional adapter members map one-to-one onto the capability flags. `RemoteCalendarEvent` / `UpsertRemoteEventInput` / `RemoteCalendarDescriptor` carry the Google-era field names verbatim, `raw` included, so the mappers and the `calendar_external_events` mirror are untouched. - `errors.ts` — `ProviderAuthError`, `ProviderGoneError`, `ProviderConflictError`, `ProviderRateLimitError` (with `retryAfterMs`), `ProviderTransientError`. Adapters translate their own wire errors into these so the engine reacts to the condition, not to one vendor's status code. `main/calendar/google/` moves to `main/calendar/providers/google/`. The Google client now implements the neutral interface; `watch`/`unwatch` are the names the engine sees, with `watchCalendar`/`stopChannel` kept as the Google-named pair the push-channel manager and the sync-server relay still speak. No behavior change: no DB schema change, no IPC contract change, no settings shape change. The existing Google tests move with the directory and are otherwise untouched — that is the correctness proof for the refactor.
|
React Doctor found no new issues. 🎉 Reviewed by React Doctor for commit |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This was referenced Aug 13, 2026
…call
Renaming `calendar/google/` to `calendar/providers/google/` restages
`push-runtime.ts`, and the scanner flagged
hashToken: async (plaintext) => createHmac('sha256', hmacKey)…
as a `high-risk-secret-assignment`. `hashToken` matches the TOKEN keyword, and
the existing arrow-function exemption rejects any quote — deliberately, so an
embedded literal stays flagged — which also rejects the string arguments of an
ordinary call.
A callee name followed by `(` is the discriminator: a bare literal body like
`() => 'sk-live-…'` has no callee and is still flagged. Both cases are covered
by new tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1391. Phase 0 of the multi-provider calendar epic #1390. Bottom of the stack.
What
The calendar layer already had an abstraction —
GoogleCalendarClient— but Google concepts leaked through it:watchCalendar/stopChannelare literally Google's push-channel API,syncCursorcarried syncToken semantics, and there was no error taxonomy at all (sync-service.tssniffedstatus === 410off Google's own wire shape).New
apps/desktop/src/main/calendar/provider/:adapter.ts—CalendarProviderAdapterplusProviderCapabilities:supportsWrite,supportsCreateCalendar,supportsPush,supportsMultiAccount,incrementalMode(sync-token|delta-link|sync-collection|ctag-etag|conditional-get|full),authFlow(oauth2|basic|url|none). Optional adapter members map one-to-one onto the capability flags, so a read-only provider simply has noupsertEvent.errors.ts—ProviderAuthError(drivesreconnect_required),ProviderGoneError(cursor invalid → full resync),ProviderConflictError(412 / etag),ProviderRateLimitError(carriesretryAfterMs),ProviderTransientError. Adapters translate their own wire errors into these so the engine reacts to the condition rather than to one vendor's status code.Renames, field names preserved verbatim (
rawincluded):GoogleCalendarRemoteEventRemoteCalendarEventGoogleCalendarUpsertEventInputUpsertRemoteEventInputGoogleCalendarDescriptorRemoteCalendarDescriptorThe Google-era names stay as aliases in
calendar/types.tsfor the existing Google call sites.main/calendar/google/→main/calendar/providers/google/. The Google client implements the neutral interface:watch/unwatchare the names the engine sees, andwatchCalendar/stopChannelare kept as the Google-named pair the push-channel manager and the sync-server relay still speak (generalized later, in #1404).Backward compatibility
No DB schema change, no IPC contract change, no settings shape change, no wire-format change. This is a type-level and file-layout refactor only —
git diff --statis 58 files, almost all of it path bumps.Verification
pnpm --filter @memry/desktop typecheck(node + web + test) — greenpnpm lint— 0 errorspnpm check:architecture/pnpm check:contracts— greenshared— 142 files, 2455 tests passedmain— 512 files, 6330 passed / 1 expected fail / 4 skippedmain-integration— 1 file, 9 passedrenderer— 609 files, 6965 passed / 6 skippedsync-service.test.ts2374 lines,client.test.ts841,oauth.test.ts840,mappers.test.ts,push-runtime.test.ts,google-channel-manager.test.ts) moved with the directory and were changed only by the relative-import depth bump. No assertion, mock or fixture was edited.provider/errors.test.ts(5 tests) covering the taxonomy.The four
calendar/google/*.test.tsentries in thetsconfig.test.node.jsonexclude backlog had their paths updated in place. I checked whether they could be dropped instead — they still fail on the pre-existingTestDbvsDataDbmismatch that put them there, which is unrelated to this change, so the backlog kept the same four entries and did not grow.Docs
MEMRY_DOCS_IMPACT_SKIP=1on push: nothing user-facing changed. No new setting, no new surface, no behavior difference — every touched file is an import path or an internal type. Docs land with #1394 (agent read-consent policy) and #1396 (cross-version compat findings).Also in this PR
fix(scripts): stop the secret scanner flagging an async arrow over a call— the directory rename restagespush-runtime.ts, and CI's Secret scan flaggedas a
high-risk-secret-assignment.hashTokenmatches the TOKEN keyword, and the existing arrow-function exemption rejects any quote — deliberately, so an embedded literal stays flagged — which also rejects the string arguments of an ordinary call. A callee name followed by(is the discriminator: a bare literal body like() => 'sk-live-…'has no callee and is still flagged. Both cases are covered by new tests inscripts/check-staged-secrets.test.mjs(19/19 green).It lives at the bottom of the stack because that is where the rename happens.