n8n-nodes-caldav-pro 3.3.0 → 3.4.1

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