n8n-nodes-caldav-pro 3.4.0 → 3.5.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/CHANGELOG.md CHANGED
@@ -1,369 +1,423 @@
1
- # Changelog
2
-
3
- All notable changes to this project are documented here. The format follows
4
- [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project
5
- adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
-
7
- ## [3.4.0]
8
-
9
- ### Fixed
10
-
11
- - **A Start or End without an offset is now read in the Timezone field's zone,
12
- not the n8n host's.** `2026-07-29T14:00:00` with Timezone `Europe/Berlin`
13
- means two o'clock in Berlin; it was previously parsed in the host's zone —
14
- usually UTC in Docker — and stored two hours out. The same class of error as
15
- the one fixed in 3.0.0, on the input side, and only when the offset was left
16
- off.
17
-
18
- This means a caller no longer has to know whether summer time applies. An
19
- explicit offset, or a trailing `Z`, is still taken at face value.
20
-
21
- If you were compensating by always writing an explicit offset, nothing
22
- changes. If you passed naive times and corrected for the shift elsewhere,
23
- remove that correction.
24
-
25
- ### Changed
26
-
27
- - The Start, End, and Timezone descriptions now say so, since those are what an
28
- AI Agent reads when deciding what to send.
29
-
30
- ## [3.3.0]
31
-
32
- ### Added
33
-
34
- - **Single occurrences of a recurring series can be changed and cancelled.**
35
- Set **Occurrence** on Delete or Update to a `recurrenceId` from a read, and
36
- only that date is affected.
37
-
38
- A series has no per-occurrence resource to address: every occurrence shares
39
- one UID and one URL. Cancelling therefore adds the slot to `EXDATE` and
40
- writes the series back — a `PUT`, not a `DELETE`, since deleting the resource
41
- would take the whole series. Changing writes a `RECURRENCE-ID` override
42
- beside the master, seeded from it so the occurrence keeps the series' details,
43
- minus the recurrence properties an override must not carry.
44
-
45
- Both anchor on the slot the rule generated rather than on where an occurrence
46
- was moved to, so a moved occurrence can still be cancelled afterwards — and
47
- cancelling removes any override for that slot instead of orphaning it.
48
-
49
- ### Changed
50
-
51
- - The guard added in 3.1.0 now points at both ways out: name an occurrence, or
52
- confirm the whole series.
53
-
54
- ## [3.2.0]
55
-
56
- ### Added
57
-
58
- - **`startLocal` and `endLocal`** on returned events: the same moment as the
59
- event's own local time with its offset applied, `2026-07-28T21:00:00+02:00`.
60
-
61
- `start` is a UTC instant because that is what sorts and computes correctly,
62
- but reading a local time off it requires knowing the offset in force on that
63
- date — and language models get that wrong. Asked to show a 19:00Z summer
64
- event in Berlin, one reported 23:00, then 20:00 on a later run: no offset,
65
- then the winter offset. For a node that advertises `usableAsTool`, handing
66
- the model arithmetic it cannot reliably do is a design fault, not the model's
67
- problem.
68
-
69
- Events stored in plain UTC carry no zone of their own and are rendered in the
70
- workflow's timezone. All-day events keep their bare date. `start` and `end`
71
- are unchanged, so nothing that reads them needs to.
72
-
73
- - The node description now tells an agent which field to use for what.
74
-
75
- ## [3.1.2]
76
-
77
- ### Fixed
78
-
79
- - **Events with a TZID were read at the wrong time when the calendar object
80
- carried a faulty VTIMEZONE.** Infomaniak's own importer writes
81
- `TZID:Europe/Berlin` with US transition rules whose `RRULE`s contradict their
82
- own `DTSTART`s. No observance then matches a summer date, ical.js falls back
83
- to a zero offset, and every affected event is reported two hours late — a
84
- 21:00 pickup shows up as 23:00.
85
-
86
- An IANA `TZID` is now resolved against the platform's timezone database
87
- rather than the embedded VTIMEZONE, which is written by whichever tool
88
- produced the event and is wrong often enough to matter. The embedded
89
- definition remains the fallback for a `TZID` that names no IANA zone, such as
90
- an Outlook-style `W. Europe Standard Time`.
91
-
92
- Anything read from an affected calendar before this release was wrong by the
93
- UTC offset in force at the event's date. Reads only — nothing this node wrote
94
- was affected.
95
-
96
- ## [3.1.1]
97
-
98
- ### Fixed
99
-
100
- - A reversed or zero-width time window is rejected instead of being sent to the
101
- server, which answers it with an empty result that reads as "no events" rather
102
- than as the mistake it is. The error shows both bounds.
103
- - A **Lookahead Days** of zero or less is rejected on Get Next. The field's
104
- `minValue` constrains the editor, not an expression that computes the value.
105
-
106
- ### Added
107
-
108
- - CI on push and pull requests: build, lint self-test, lint, tests, the offline
109
- smoke test, and a check on the contents of the published tarball. On a version
110
- tag, the tag is compared against `package.json`.
111
- - `npm run check:package`, also wired into `prepublishOnly`.
112
- - `engines: node >=20.15`, matching what the shipped code needs. Development
113
- requires Node 22 — `n8n-workflow` pulls in `isolated-vm`, which will not
114
- install on 20 — but that does not reach consumers.
115
-
116
- ## [3.1.0]
117
-
118
- ### Added
119
-
120
- - **Entire Series** toggle on Delete and Update. Every occurrence of a
121
- recurring event shares one UID and one resource, so deleting "tomorrow's
122
- standup" by UID removes the whole series. That now has to be asked for: if
123
- the target turns out to be recurring and the toggle is off, the operation is
124
- refused and the error names the rule at stake.
125
- - Delete sends `If-Match` with the stored ETag, so an event modified since it
126
- was read is not removed unseen. A `412` is reported as a conflict rather than
127
- passed through raw.
128
-
129
- ### Changed
130
-
131
- - **Delete on a recurring event now fails by default.** Previously it removed
132
- every occurrence silently. Set **Entire Series** to restore the old
133
- behaviour. Non-recurring events are unaffected.
134
- - Delete reads the event before removing it — one extra request, which is what
135
- makes both the series check and the conditional delete possible. A missing
136
- event is now reported as such instead of surfacing as a bare `404`.
137
-
138
- ## [3.0.0]
139
-
140
- The first release since 2.6.1. Versions 2.7.0 through 2.11.0 were developed but
141
- never published; their entries are kept below for traceability. If you are
142
- upgrading from 2.6.x, this section is what matters.
143
-
144
- ### BREAKING CHANGES
145
-
146
- 1. **`start` and `end` changed format.** Timed events previously reported a
147
- local wall clock with no offset (`2026-04-20T14:00:00`), which downstream
148
- nodes re-read in the n8n host's timezone. They are now explicit UTC instants
149
- (`2026-04-20T12:00:00.000Z`); all-day events report a bare date
150
- (`2026-04-20`). Both sort correctly as strings. Any expression that compares,
151
- slices, or parses these values needs review.
152
-
153
- 2. **`raw` is no longer returned by default.** Read operations have a
154
- **Simplify** toggle, on by default, which omits the full `VCALENDAR` source.
155
- Turn it off where you depend on `raw`.
156
-
157
- 3. **Update no longer creates a missing event.** It reads the stored object
158
- first in order to preserve fields you did not supply, so a missing event is
159
- now an error instead of a silent create. Use Create for that.
160
-
161
- 4. **Events are written at different times than before — this is the fix, but
162
- it will move existing workflows.** With **Timezone** set, 2.6.x emitted the
163
- n8n host's wall clock under the requested `TZID`, shifting every event by the
164
- host's UTC offset (two hours on a UTC host writing `Europe/Berlin`). All-day
165
- events were written one calendar day early. If you compensated for either by
166
- pre-shifting your input, remove that workaround or events will now be wrong
167
- in the other direction.
168
-
169
- 5. **All-day `DTEND` is now the exclusive end RFC 5545 requires.** A same-day
170
- or end-of-day range used to encode a zero-length event; it now produces a
171
- one-day event.
172
-
173
- ### Added
174
-
175
- - Recurring series are expanded into the occurrences inside the queried window,
176
- honouring `EXDATE` and `RECURRENCE-ID` overrides.
177
- - Events can be addressed by **Event URL** as well as UID, and a UID is
178
- resolved against the server — so events created in Thunderbird, Apple
179
- Calendar, or a web UI can be reached at all.
180
- - Calendars report `readOnly`, and read-only ones are marked 🔒 in the dropdown.
181
- - `recurrenceId` and `timezone` on returned events.
182
- - A test suite: 80 unit and integration tests, plus a smoke test that runs
183
- against a real server and one that runs offline.
184
-
185
- ### Fixed
186
-
187
- - Update preserves everything it was not asked to change, including properties
188
- this node does not model (`CATEGORIES`, `ORGANIZER`, custom `X-` properties),
189
- and is guarded by `If-Match` against concurrent edits.
190
- - A `TZID` with no accompanying `VTIMEZONE` is resolved against the named IANA
191
- zone instead of the host's.
192
- - Writing to a read-only calendar explains itself instead of returning n8n's
193
- generic "check your credentials".
194
-
195
- ### Security
196
-
197
- - Advisories affecting installed users: **0**. `fast-xml-parser` moves 4 → 5.
198
-
199
- ## [2.11.0]
200
-
201
- ### Added
202
-
203
- - Calendars report **`readOnly`**, taken from the server's
204
- `current-user-privilege-set`. It rides along on the listing discovery already
205
- performs, so it costs no extra request.
206
- - Read-only calendars are marked `🔒 … (Read-Only)` in the Calendar dropdown
207
- and sorted after the writable ones.
208
-
209
- ### Fixed
210
-
211
- - Writing to a calendar you cannot write to produced n8n's generic
212
- `Forbidden - perhaps check your credentials?`, which points at the wrong
213
- cause entirely. Such a 403 now explains that the calendar is read-only —
214
- typically one shared with you, or a subscribed feed — and how to find one
215
- that is not.
216
-
217
- ### Notes
218
-
219
- - A server that does not report privileges leaves `readOnly` undefined rather
220
- than assuming read-only, so nothing is mislabelled or blocked on servers
221
- without the property.
222
- - Found by running the smoke test against a real account, where five of six
223
- calendars turned out to be read-only while looking identical in the dropdown.
224
-
225
- ## [2.10.0]
226
-
227
- ### Changed
228
-
229
- - **gulp removed.** A single task copied one SVG into the build output; gulp 4
230
- is end-of-life and pulled in gulp-cli, liftoff, matchdep, chokidar, and
231
- micromatch to do it. Replaced by `scripts/copy-icons.mjs`, which reproduces
232
- the previous behaviour exactly.
233
- - **ESLint 8 → 9**, with a flat config (`eslint.config.mjs`) replacing
234
- `.eslintrc.js`. `@typescript-eslint/parser` moves 7 → 8.
235
- - **`fast-xml-parser` 4 → 5** — a runtime dependency, and the only advisory
236
- that reached installed users.
237
-
238
- ### Added
239
-
240
- - `npm run lint:selftest`, which feeds a deliberate violation to each
241
- eslint-plugin-n8n-nodes-base preset and fails if it goes unreported. Part of
242
- `prepublishOnly`.
243
-
244
- ### Removed
245
-
246
- - The plugin's `community` preset (19 rules against `package.json`) is no
247
- longer configured. Its rules visit `ObjectExpression` while the JSON parser
248
- emits `JSONObjectExpression`, so they never matched — verified inert under
249
- ESLint 8 and 9 and under plugin 1.16.6 and 1.16.7. It was never enforcing
250
- anything; the configuration only made it look as though it was.
251
-
252
- ### Security
253
-
254
- - Advisories affecting installed users: **0** (was 1 moderate). Development
255
- advisories drop from 24 to 5, all in ESLint's own `minimatch` chain. ESLint 10
256
- would clear those but crashes eslint-plugin-n8n-nodes-base during traversal,
257
- so the project stays on 9.
258
-
259
- ## [2.9.2]
260
-
261
- ### Added
262
-
263
- - `LICENSE` file. The package declared MIT but shipped no licence text.
264
- - `CHANGELOG.md` is now included in the published package.
265
-
266
- ### Fixed
267
-
268
- - `dist/tsconfig.tsbuildinfo`, a 64 kB incremental build cache, was published
269
- with every release. The package is now 37 kB instead of 54 kB.
270
-
271
- ## [2.9.1]
272
-
273
- ### Added
274
-
275
- - Calendar discovery is memoised per execution. A batch of input items used to
276
- repeat the full PROPFIND chain for every item; it now runs once.
277
- - `Time Min` / `Time Max` are validated, so an unparseable date fails with a
278
- message naming the field instead of an opaque `400` from the server.
279
- - Integration tests covering the read operations end to end against a fake
280
- CalDAV server.
281
-
282
- ### Changed
283
-
284
- - Internal: `Get Many`, `Get Next`, and `Search` now share one implementation
285
- and differ only in the time window and filter predicate.
286
-
287
- ## [2.9.0]
288
-
289
- ### Added
290
-
291
- - **Event URL** field on `Get`, `Update`, `Delete`, and `Move`. Read operations
292
- already return `url`; passing it through addresses the event directly.
293
- - When only a UID is known, the event is located with a `calendar-query` on the
294
- UID, falling back to the historical `<calendar>/<uid>.ics` convention.
295
- - **Simplify** toggle on the read operations, on by default.
296
-
297
- ### Fixed
298
-
299
- - `Get` / `Update` / `Delete` / `Move` could only reach events created by this
300
- node. Events added in Thunderbird, Apple Calendar, or a web UI are stored
301
- under a filename the server chose, so addressing them by UID returned `404`.
302
-
303
- ### Changed
304
-
305
- - **Event UID** is no longer mandatory — either UID or URL identifies an event.
306
- - `raw` is omitted by default. Turn **Simplify** off to restore it.
307
-
308
- ## [2.8.0]
309
-
310
- ### Added
311
-
312
- - Recurring series are expanded into the individual occurrences that fall
313
- inside the queried window, honouring `EXDATE` exclusions and `RECURRENCE-ID`
314
- overrides.
315
- - `recurrenceId` and `timezone` fields on returned events.
316
-
317
- ### Fixed
318
-
319
- - A series reported its original `DTSTART` rather than the occurrence in the
320
- requested window, and `Get Next` skipped recurring events entirely.
321
- - A `TZID` without an accompanying `VTIMEZONE` was read in the n8n host's
322
- timezone. Such times are now resolved against the named IANA zone.
323
- - Reading an object whose overridden instance was serialised first returned the
324
- override instead of the series master.
325
-
326
- ### Changed
327
-
328
- - **`start` / `end` output format.** Timed events previously reported a local
329
- wall clock with no offset (`2026-04-20T14:00:00`), which downstream nodes
330
- re-read in the host's timezone. They are now explicit UTC instants
331
- (`2026-04-20T12:00:00.000Z`); all-day events report a bare date
332
- (`2026-04-20`). Both sort correctly as strings.
333
-
334
- ## [2.7.0]
335
-
336
- ### Fixed
337
-
338
- - **Timed events were written at the wrong time.** With **Timezone** set, the
339
- n8n host's wall clock was emitted under the requested `TZID`, shifting every
340
- event by the host's UTC offset — two hours on a UTC host writing
341
- `Europe/Berlin`. Events created with an earlier version should be re-checked.
342
- - **All-day events were written one day early**, because the date was taken
343
- from the instant's UTC components.
344
- - All-day `DTEND` is now the exclusive end RFC 5545 requires. An inclusive
345
- same-day end produces a one-day event instead of a zero-length one.
346
- - **`Update` no longer discards data.** It rebuilt the event from the node's
347
- own fields, dropping everything not restated — description, location,
348
- attendees, reminders, recurrence — along with properties this node does not
349
- model at all (`CATEGORIES`, `ORGANIZER`, custom `X-` properties). Updates are
350
- now applied as a patch to the stored object.
351
-
352
- ### Added
353
-
354
- - `If-Match` on update, so a concurrent edit is rejected rather than
355
- overwritten.
356
- - `SEQUENCE` is incremented and `LAST-MODIFIED` set on every update, so clients
357
- recognise the change.
358
- - Unit test suite (`npm test`, Vitest). Tests assert that output is identical
359
- across several host timezones — the class of bug fixed in this release.
360
-
361
- ### Changed
362
-
363
- - `Update` fails when the event does not exist instead of silently creating it.
364
- - iCalendar is serialised through ical.js, which brings RFC-compliant line
365
- folding and parameter quoting.
366
-
367
- ## [2.0.0]
368
-
369
- Initial release.
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+
11
+ - **External ICS Feed** read-only resource for subscribed `https://`, `webcal://`,
12
+ and `webcals://` calendars. It supports Get Many, Get Next, and Search while
13
+ preserving recurring-event, timezone, all-day, and reminder parsing.
14
+ - A separate **ICS Feed API** credential stores token-bearing subscription links
15
+ as a secret. Feed events expose `source: "icsFeed"` and an optional Feed Name,
16
+ never the subscription URL.
17
+
18
+ ### Security
19
+
20
+ - External feed requests never use CalDAV authentication and send no credentials
21
+ or cookies. Only public HTTPS hosts on port 443 are accepted; local/private
22
+ names, IP literals, custom ports, and cross-origin or unsafe redirects are
23
+ rejected without exposing the feed URL in errors or workflow output.
24
+
25
+ ## [3.4.1]
26
+
27
+ ### Security
28
+
29
+ - CalDAV URLs supplied through Calendar, Target Calendar, Event URL, or the
30
+ credential Default Calendar must stay on the configured server origin, so
31
+ credential-backed requests cannot be redirected to another host by free-text
32
+ input.
33
+
34
+ ### Fixed
35
+
36
+ - Weak ETags returned by CalDAV servers are no longer used as invalid `If-Match`
37
+ headers on Update, Delete, or Move; strong ETags remain conditional.
38
+ - Move now reports the partial-success state clearly when the target copy was
39
+ created but deleting the source failed, including both URLs and a cleanup hint.
40
+ - Get Next keeps today's and ongoing multi-day all-day events, respecting
41
+ exclusive `DTEND` and the workflow timezone.
42
+ - Timed Create and Update reject ranges where End is not after Start; all-day
43
+ date behavior is unchanged.
44
+ - `EMAIL` VALARMs are only written in RFC-compliant form with `SUMMARY` and
45
+ `ATTENDEE`; reminders without event attendees fall back to `DISPLAY`.
46
+ - A report error from an explicitly selected calendar is reported instead of
47
+ being treated as an empty result.
48
+ - Update now patches the stored series master/resource instead of rebuilding from
49
+ the first expanded VEVENT instance.
50
+ - Create names a missing or invalid Start/End field instead of failing later with
51
+ an ambiguous date error.
52
+
53
+ ### Changed
54
+
55
+ - Update is a true patch: omitted Summary, Start, End, and additional fields keep
56
+ their stored values instead of being rewritten with defaults such as `$now`.
57
+ - Package checks now guard shipped node metadata and any declared `main`; the
58
+ unused `main` entry was removed and the node metadata uses this community
59
+ package's namespace.
60
+
61
+ ## [3.4.0]
62
+
63
+ ### Fixed
64
+
65
+ - **A Start or End without an offset is now read in the Timezone field's zone,
66
+ not the n8n host's.** `2026-07-29T14:00:00` with Timezone `Europe/Berlin`
67
+ means two o'clock in Berlin; it was previously parsed in the host's zone —
68
+ usually UTC in Docker — and stored two hours out. The same class of error as
69
+ the one fixed in 3.0.0, on the input side, and only when the offset was left
70
+ off.
71
+
72
+ This means a caller no longer has to know whether summer time applies. An
73
+ explicit offset, or a trailing `Z`, is still taken at face value.
74
+
75
+ If you were compensating by always writing an explicit offset, nothing
76
+ changes. If you passed naive times and corrected for the shift elsewhere,
77
+ remove that correction.
78
+
79
+ ### Changed
80
+
81
+ - The Start, End, and Timezone descriptions now say so, since those are what an
82
+ AI Agent reads when deciding what to send.
83
+
84
+ ## [3.3.0]
85
+
86
+ ### Added
87
+
88
+ - **Single occurrences of a recurring series can be changed and cancelled.**
89
+ Set **Occurrence** on Delete or Update to a `recurrenceId` from a read, and
90
+ only that date is affected.
91
+
92
+ A series has no per-occurrence resource to address: every occurrence shares
93
+ one UID and one URL. Cancelling therefore adds the slot to `EXDATE` and
94
+ writes the series back — a `PUT`, not a `DELETE`, since deleting the resource
95
+ would take the whole series. Changing writes a `RECURRENCE-ID` override
96
+ beside the master, seeded from it so the occurrence keeps the series' details,
97
+ minus the recurrence properties an override must not carry.
98
+
99
+ Both anchor on the slot the rule generated rather than on where an occurrence
100
+ was moved to, so a moved occurrence can still be cancelled afterwards — and
101
+ cancelling removes any override for that slot instead of orphaning it.
102
+
103
+ ### Changed
104
+
105
+ - The guard added in 3.1.0 now points at both ways out: name an occurrence, or
106
+ confirm the whole series.
107
+
108
+ ## [3.2.0]
109
+
110
+ ### Added
111
+
112
+ - **`startLocal` and `endLocal`** on returned events: the same moment as the
113
+ event's own local time with its offset applied, `2026-07-28T21:00:00+02:00`.
114
+
115
+ `start` is a UTC instant because that is what sorts and computes correctly,
116
+ but reading a local time off it requires knowing the offset in force on that
117
+ date — and language models get that wrong. Asked to show a 19:00Z summer
118
+ event in Berlin, one reported 23:00, then 20:00 on a later run: no offset,
119
+ then the winter offset. For a node that advertises `usableAsTool`, handing
120
+ the model arithmetic it cannot reliably do is a design fault, not the model's
121
+ problem.
122
+
123
+ Events stored in plain UTC carry no zone of their own and are rendered in the
124
+ workflow's timezone. All-day events keep their bare date. `start` and `end`
125
+ are unchanged, so nothing that reads them needs to.
126
+
127
+ - The node description now tells an agent which field to use for what.
128
+
129
+ ## [3.1.2]
130
+
131
+ ### Fixed
132
+
133
+ - **Events with a TZID were read at the wrong time when the calendar object
134
+ carried a faulty VTIMEZONE.** Infomaniak's own importer writes
135
+ `TZID:Europe/Berlin` with US transition rules whose `RRULE`s contradict their
136
+ own `DTSTART`s. No observance then matches a summer date, ical.js falls back
137
+ to a zero offset, and every affected event is reported two hours late — a
138
+ 21:00 pickup shows up as 23:00.
139
+
140
+ An IANA `TZID` is now resolved against the platform's timezone database
141
+ rather than the embedded VTIMEZONE, which is written by whichever tool
142
+ produced the event and is wrong often enough to matter. The embedded
143
+ definition remains the fallback for a `TZID` that names no IANA zone, such as
144
+ an Outlook-style `W. Europe Standard Time`.
145
+
146
+ Anything read from an affected calendar before this release was wrong by the
147
+ UTC offset in force at the event's date. Reads only — nothing this node wrote
148
+ was affected.
149
+
150
+ ## [3.1.1]
151
+
152
+ ### Fixed
153
+
154
+ - A reversed or zero-width time window is rejected instead of being sent to the
155
+ server, which answers it with an empty result that reads as "no events" rather
156
+ than as the mistake it is. The error shows both bounds.
157
+ - A **Lookahead Days** of zero or less is rejected on Get Next. The field's
158
+ `minValue` constrains the editor, not an expression that computes the value.
159
+
160
+ ### Added
161
+
162
+ - CI on push and pull requests: build, lint self-test, lint, tests, the offline
163
+ smoke test, and a check on the contents of the published tarball. On a version
164
+ tag, the tag is compared against `package.json`.
165
+ - `npm run check:package`, also wired into `prepublishOnly`.
166
+ - `engines: node >=20.15`, matching what the shipped code needs. Development
167
+ requires Node 22 — `n8n-workflow` pulls in `isolated-vm`, which will not
168
+ install on 20 — but that does not reach consumers.
169
+
170
+ ## [3.1.0]
171
+
172
+ ### Added
173
+
174
+ - **Entire Series** toggle on Delete and Update. Every occurrence of a
175
+ recurring event shares one UID and one resource, so deleting "tomorrow's
176
+ standup" by UID removes the whole series. That now has to be asked for: if
177
+ the target turns out to be recurring and the toggle is off, the operation is
178
+ refused and the error names the rule at stake.
179
+ - Delete sends `If-Match` with the stored ETag, so an event modified since it
180
+ was read is not removed unseen. A `412` is reported as a conflict rather than
181
+ passed through raw.
182
+
183
+ ### Changed
184
+
185
+ - **Delete on a recurring event now fails by default.** Previously it removed
186
+ every occurrence silently. Set **Entire Series** to restore the old
187
+ behaviour. Non-recurring events are unaffected.
188
+ - Delete reads the event before removing it — one extra request, which is what
189
+ makes both the series check and the conditional delete possible. A missing
190
+ event is now reported as such instead of surfacing as a bare `404`.
191
+
192
+ ## [3.0.0]
193
+
194
+ The first release since 2.6.1. Versions 2.7.0 through 2.11.0 were developed but
195
+ never published; their entries are kept below for traceability. If you are
196
+ upgrading from 2.6.x, this section is what matters.
197
+
198
+ ### BREAKING CHANGES
199
+
200
+ 1. **`start` and `end` changed format.** Timed events previously reported a
201
+ local wall clock with no offset (`2026-04-20T14:00:00`), which downstream
202
+ nodes re-read in the n8n host's timezone. They are now explicit UTC instants
203
+ (`2026-04-20T12:00:00.000Z`); all-day events report a bare date
204
+ (`2026-04-20`). Both sort correctly as strings. Any expression that compares,
205
+ slices, or parses these values needs review.
206
+
207
+ 2. **`raw` is no longer returned by default.** Read operations have a
208
+ **Simplify** toggle, on by default, which omits the full `VCALENDAR` source.
209
+ Turn it off where you depend on `raw`.
210
+
211
+ 3. **Update no longer creates a missing event.** It reads the stored object
212
+ first in order to preserve fields you did not supply, so a missing event is
213
+ now an error instead of a silent create. Use Create for that.
214
+
215
+ 4. **Events are written at different times than before — this is the fix, but
216
+ it will move existing workflows.** With **Timezone** set, 2.6.x emitted the
217
+ n8n host's wall clock under the requested `TZID`, shifting every event by the
218
+ host's UTC offset (two hours on a UTC host writing `Europe/Berlin`). All-day
219
+ events were written one calendar day early. If you compensated for either by
220
+ pre-shifting your input, remove that workaround or events will now be wrong
221
+ in the other direction.
222
+
223
+ 5. **All-day `DTEND` is now the exclusive end RFC 5545 requires.** A same-day
224
+ or end-of-day range used to encode a zero-length event; it now produces a
225
+ one-day event.
226
+
227
+ ### Added
228
+
229
+ - Recurring series are expanded into the occurrences inside the queried window,
230
+ honouring `EXDATE` and `RECURRENCE-ID` overrides.
231
+ - Events can be addressed by **Event URL** as well as UID, and a UID is
232
+ resolved against the server — so events created in Thunderbird, Apple
233
+ Calendar, or a web UI can be reached at all.
234
+ - Calendars report `readOnly`, and read-only ones are marked 🔒 in the dropdown.
235
+ - `recurrenceId` and `timezone` on returned events.
236
+ - A test suite: 80 unit and integration tests, plus a smoke test that runs
237
+ against a real server and one that runs offline.
238
+
239
+ ### Fixed
240
+
241
+ - Update preserves everything it was not asked to change, including properties
242
+ this node does not model (`CATEGORIES`, `ORGANIZER`, custom `X-` properties),
243
+ and is guarded by `If-Match` against concurrent edits.
244
+ - A `TZID` with no accompanying `VTIMEZONE` is resolved against the named IANA
245
+ zone instead of the host's.
246
+ - Writing to a read-only calendar explains itself instead of returning n8n's
247
+ generic "check your credentials".
248
+
249
+ ### Security
250
+
251
+ - Advisories affecting installed users: **0**. `fast-xml-parser` moves 4 → 5.
252
+
253
+ ## [2.11.0]
254
+
255
+ ### Added
256
+
257
+ - Calendars report **`readOnly`**, taken from the server's
258
+ `current-user-privilege-set`. It rides along on the listing discovery already
259
+ performs, so it costs no extra request.
260
+ - Read-only calendars are marked `🔒 … (Read-Only)` in the Calendar dropdown
261
+ and sorted after the writable ones.
262
+
263
+ ### Fixed
264
+
265
+ - Writing to a calendar you cannot write to produced n8n's generic
266
+ `Forbidden - perhaps check your credentials?`, which points at the wrong
267
+ cause entirely. Such a 403 now explains that the calendar is read-only —
268
+ typically one shared with you, or a subscribed feed — and how to find one
269
+ that is not.
270
+
271
+ ### Notes
272
+
273
+ - A server that does not report privileges leaves `readOnly` undefined rather
274
+ than assuming read-only, so nothing is mislabelled or blocked on servers
275
+ without the property.
276
+ - Found by running the smoke test against a real account, where five of six
277
+ calendars turned out to be read-only while looking identical in the dropdown.
278
+
279
+ ## [2.10.0]
280
+
281
+ ### Changed
282
+
283
+ - **gulp removed.** A single task copied one SVG into the build output; gulp 4
284
+ is end-of-life and pulled in gulp-cli, liftoff, matchdep, chokidar, and
285
+ micromatch to do it. Replaced by `scripts/copy-icons.mjs`, which reproduces
286
+ the previous behaviour exactly.
287
+ - **ESLint 8 → 9**, with a flat config (`eslint.config.mjs`) replacing
288
+ `.eslintrc.js`. `@typescript-eslint/parser` moves 7 → 8.
289
+ - **`fast-xml-parser` 4 → 5** — a runtime dependency, and the only advisory
290
+ that reached installed users.
291
+
292
+ ### Added
293
+
294
+ - `npm run lint:selftest`, which feeds a deliberate violation to each
295
+ eslint-plugin-n8n-nodes-base preset and fails if it goes unreported. Part of
296
+ `prepublishOnly`.
297
+
298
+ ### Removed
299
+
300
+ - The plugin's `community` preset (19 rules against `package.json`) is no
301
+ longer configured. Its rules visit `ObjectExpression` while the JSON parser
302
+ emits `JSONObjectExpression`, so they never matched — verified inert under
303
+ ESLint 8 and 9 and under plugin 1.16.6 and 1.16.7. It was never enforcing
304
+ anything; the configuration only made it look as though it was.
305
+
306
+ ### Security
307
+
308
+ - Advisories affecting installed users: **0** (was 1 moderate). Development
309
+ advisories drop from 24 to 5, all in ESLint's own `minimatch` chain. ESLint 10
310
+ would clear those but crashes eslint-plugin-n8n-nodes-base during traversal,
311
+ so the project stays on 9.
312
+
313
+ ## [2.9.2]
314
+
315
+ ### Added
316
+
317
+ - `LICENSE` file. The package declared MIT but shipped no licence text.
318
+ - `CHANGELOG.md` is now included in the published package.
319
+
320
+ ### Fixed
321
+
322
+ - `dist/tsconfig.tsbuildinfo`, a 64 kB incremental build cache, was published
323
+ with every release. The package is now 37 kB instead of 54 kB.
324
+
325
+ ## [2.9.1]
326
+
327
+ ### Added
328
+
329
+ - Calendar discovery is memoised per execution. A batch of input items used to
330
+ repeat the full PROPFIND chain for every item; it now runs once.
331
+ - `Time Min` / `Time Max` are validated, so an unparseable date fails with a
332
+ message naming the field instead of an opaque `400` from the server.
333
+ - Integration tests covering the read operations end to end against a fake
334
+ CalDAV server.
335
+
336
+ ### Changed
337
+
338
+ - Internal: `Get Many`, `Get Next`, and `Search` now share one implementation
339
+ and differ only in the time window and filter predicate.
340
+
341
+ ## [2.9.0]
342
+
343
+ ### Added
344
+
345
+ - **Event URL** field on `Get`, `Update`, `Delete`, and `Move`. Read operations
346
+ already return `url`; passing it through addresses the event directly.
347
+ - When only a UID is known, the event is located with a `calendar-query` on the
348
+ UID, falling back to the historical `<calendar>/<uid>.ics` convention.
349
+ - **Simplify** toggle on the read operations, on by default.
350
+
351
+ ### Fixed
352
+
353
+ - `Get` / `Update` / `Delete` / `Move` could only reach events created by this
354
+ node. Events added in Thunderbird, Apple Calendar, or a web UI are stored
355
+ under a filename the server chose, so addressing them by UID returned `404`.
356
+
357
+ ### Changed
358
+
359
+ - **Event UID** is no longer mandatory — either UID or URL identifies an event.
360
+ - `raw` is omitted by default. Turn **Simplify** off to restore it.
361
+
362
+ ## [2.8.0]
363
+
364
+ ### Added
365
+
366
+ - Recurring series are expanded into the individual occurrences that fall
367
+ inside the queried window, honouring `EXDATE` exclusions and `RECURRENCE-ID`
368
+ overrides.
369
+ - `recurrenceId` and `timezone` fields on returned events.
370
+
371
+ ### Fixed
372
+
373
+ - A series reported its original `DTSTART` rather than the occurrence in the
374
+ requested window, and `Get Next` skipped recurring events entirely.
375
+ - A `TZID` without an accompanying `VTIMEZONE` was read in the n8n host's
376
+ timezone. Such times are now resolved against the named IANA zone.
377
+ - Reading an object whose overridden instance was serialised first returned the
378
+ override instead of the series master.
379
+
380
+ ### Changed
381
+
382
+ - **`start` / `end` output format.** Timed events previously reported a local
383
+ wall clock with no offset (`2026-04-20T14:00:00`), which downstream nodes
384
+ re-read in the host's timezone. They are now explicit UTC instants
385
+ (`2026-04-20T12:00:00.000Z`); all-day events report a bare date
386
+ (`2026-04-20`). Both sort correctly as strings.
387
+
388
+ ## [2.7.0]
389
+
390
+ ### Fixed
391
+
392
+ - **Timed events were written at the wrong time.** With **Timezone** set, the
393
+ n8n host's wall clock was emitted under the requested `TZID`, shifting every
394
+ event by the host's UTC offset — two hours on a UTC host writing
395
+ `Europe/Berlin`. Events created with an earlier version should be re-checked.
396
+ - **All-day events were written one day early**, because the date was taken
397
+ from the instant's UTC components.
398
+ - All-day `DTEND` is now the exclusive end RFC 5545 requires. An inclusive
399
+ same-day end produces a one-day event instead of a zero-length one.
400
+ - **`Update` no longer discards data.** It rebuilt the event from the node's
401
+ own fields, dropping everything not restated — description, location,
402
+ attendees, reminders, recurrence — along with properties this node does not
403
+ model at all (`CATEGORIES`, `ORGANIZER`, custom `X-` properties). Updates are
404
+ now applied as a patch to the stored object.
405
+
406
+ ### Added
407
+
408
+ - `If-Match` on update, so a concurrent edit is rejected rather than
409
+ overwritten.
410
+ - `SEQUENCE` is incremented and `LAST-MODIFIED` set on every update, so clients
411
+ recognise the change.
412
+ - Unit test suite (`npm test`, Vitest). Tests assert that output is identical
413
+ across several host timezones — the class of bug fixed in this release.
414
+
415
+ ### Changed
416
+
417
+ - `Update` fails when the event does not exist instead of silently creating it.
418
+ - iCalendar is serialised through ical.js, which brings RFC-compliant line
419
+ folding and parameter quoting.
420
+
421
+ ## [2.0.0]
422
+
423
+ Initial release.