Files
familysync/apps/api/src/broker/spike.ts
T
Lucas Berger 982438dc10 style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
2026-06-11 20:35:18 -04:00

76 lines
2.9 KiB
TypeScript

/**
* CAL-08 spike script: enumerate Fastmail calendar collections for a single account.
*
* Purpose: Confirm that a Fastmail app password ("Mail, Contacts & Calendars" scope)
* can read BOTH the shared family calendar AND Lucas's personal calendar via
* CalDAV PROPFIND. This is the go/no-go gate for the N-credential per-member model
* described in D-02/D-05.
*
* Run:
* cd apps/api
* FASTMAIL_EMAIL=lucas@fastmail.com \
* FASTMAIL_APP_PASSWORD=<app-pw> \
* pnpm exec tsx src/broker/spike.ts
*
* Output: prints each returned calendar collection — url, displayName, ctag, syncToken.
* Expected: shared family calendar + Lucas's personal calendar both appear.
*
* Security (T-04-04):
* - App password is read from env, never logged.
* - Output prints only calendar URLs and display names, not the password.
* - This script is dev-only; NOT imported by the API or Docker image (T-04-SC).
*
* After running, record results in:
* .planning/phases/01-foundation-broker-spike/CAL-08-DECISION.md
*/
import { createFastmailClient } from './client.js';
async function main() {
const email = process.env.FASTMAIL_EMAIL;
const appPassword = process.env.FASTMAIL_APP_PASSWORD;
if (!email || !appPassword) {
console.error(
'Usage: FASTMAIL_EMAIL=<email> FASTMAIL_APP_PASSWORD=<pw> pnpm exec tsx src/broker/spike.ts',
);
process.exit(1);
}
console.log(`[CAL-08 spike] Connecting to Fastmail CalDAV as: ${email}`);
console.log('[CAL-08 spike] Creating tsdav client...');
const client = await createFastmailClient(email, appPassword);
console.log('[CAL-08 spike] Fetching calendars via PROPFIND...');
const calendars = await client.fetchCalendars();
console.log(`\n[CAL-08 spike] Found ${calendars.length} calendar collection(s):\n`);
for (const cal of calendars) {
console.log('---');
console.log(` url: ${cal.url}`);
// displayName may be a string or a Record (language-tagged value) per CalDAV spec
const displayName =
typeof cal.displayName === 'string'
? cal.displayName
: JSON.stringify(cal.displayName ?? '(none)');
console.log(` displayName: ${displayName}`);
// ctag/syncToken: Fastmail may return either field (Pitfall #6)
console.log(` ctag: ${(cal as { ctag?: string }).ctag ?? '(not returned)'}`);
console.log(` syncToken: ${(cal as { syncToken?: string }).syncToken ?? '(not returned)'}`);
}
console.log('\n[CAL-08 spike] Done.');
console.log('\nNext steps:');
console.log(' 1. Confirm the shared family calendar URL appears above.');
console.log(" 2. Confirm Lucas's personal calendar URL appears above.");
console.log(' 3. Record both URLs and ctag/syncToken findings in:');
console.log(' .planning/phases/01-foundation-broker-spike/CAL-08-DECISION.md');
}
main().catch((err: unknown) => {
console.error('[CAL-08 spike] Fatal error:', err instanceof Error ? err.message : String(err));
process.exit(1);
});