Why getEvents requires a date range
There is no CalendarApp method that returns all events unconditionally. getEvents always takes two Date objects as its first two arguments — a start (inclusive) and an end (exclusive). Pass JavaScript Date objects, not strings; the API will silently return nothing if you hand it a string, which is a frustrating first hour of debugging.
The practical consequence is that you must decide your window upfront. For a full-year export, new Date('2024-01-01') and new Date('2024-12-31') work fine. For a rolling 90-day window you can compute them: var end = new Date(); var start = new Date(end - 90 * 24 * 60 * 60 * 1000). The calendar quota is 500 events returned per call, so for dense calendars you will need to page by breaking the range into monthly chunks and concatenating the arrays.
getCalendarsByName returns an array, even when there is exactly one match, so index [0] is required. If the name does not match precisely (case and punctuation count), the array is empty and cal will be undefined — add a guard in production: if (!cal) { throw new Error('Calendar not found: ' + CALENDAR_NAME); }.