caldav-adapter 4.3.1 → 5.0.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.
Files changed (94) hide show
  1. package/.commitlintrc.js +3 -0
  2. package/.editorconfig +9 -0
  3. package/.gitattributes +1 -0
  4. package/.github/workflows/ci.yml +24 -0
  5. package/.husky/commit-msg +4 -0
  6. package/.husky/pre-commit +4 -0
  7. package/.lintstagedrc.js +5 -0
  8. package/.prettierrc.js +5 -0
  9. package/.remarkignore +1 -0
  10. package/.remarkrc.js +3 -0
  11. package/.xo-config.js +8 -0
  12. package/LICENSE +21 -0
  13. package/README.md +26 -240
  14. package/common/parse-body.js +16 -0
  15. package/common/response.js +51 -0
  16. package/common/tags.js +412 -0
  17. package/common/winston.js +32 -0
  18. package/common/x-build.js +109 -0
  19. package/common/xml.js +36 -0
  20. package/index.js +171 -0
  21. package/package.json +58 -40
  22. package/routes/calendar/calendar/calendar-multiget.js +48 -0
  23. package/routes/calendar/calendar/calendar-query.js +80 -0
  24. package/routes/calendar/calendar/delete.js +40 -0
  25. package/routes/calendar/calendar/event-response.js +37 -0
  26. package/routes/calendar/calendar/expand-property.js +11 -0
  27. package/routes/calendar/calendar/get.js +27 -0
  28. package/routes/calendar/calendar/propfind.js +73 -0
  29. package/routes/calendar/calendar/proppatch.js +32 -0
  30. package/routes/calendar/calendar/put.js +76 -0
  31. package/routes/calendar/calendar/report.js +36 -0
  32. package/routes/calendar/calendar/sync-collection.js +36 -0
  33. package/routes/calendar/calendar.js +80 -0
  34. package/routes/calendar/user/propfind.js +60 -0
  35. package/routes/calendar/user/proppatch.js +31 -0
  36. package/routes/principal/mkcalendar.js +69 -0
  37. package/routes/principal/principal.js +33 -0
  38. package/routes/principal/propfind.js +32 -0
  39. package/routes/principal/report.js +27 -0
  40. package/test/test.js +8 -0
  41. package/lib/common/date.d.ts +0 -2
  42. package/lib/common/date.js +0 -11
  43. package/lib/common/eventBuild.d.ts +0 -6
  44. package/lib/common/eventBuild.js +0 -170
  45. package/lib/common/parseBody.d.ts +0 -2
  46. package/lib/common/parseBody.js +0 -21
  47. package/lib/common/response.d.ts +0 -6
  48. package/lib/common/response.js +0 -53
  49. package/lib/common/tags.d.ts +0 -263
  50. package/lib/common/tags.js +0 -337
  51. package/lib/common/winston.d.ts +0 -11
  52. package/lib/common/winston.js +0 -26
  53. package/lib/common/xBuild.d.ts +0 -17
  54. package/lib/common/xBuild.js +0 -80
  55. package/lib/common/xml.d.ts +0 -17
  56. package/lib/common/xml.js +0 -30
  57. package/lib/index.d.ts +0 -143
  58. package/lib/index.js +0 -8
  59. package/lib/koa.d.ts +0 -21
  60. package/lib/koa.js +0 -130
  61. package/lib/routes/calendar/calendar/calendar-multiget.d.ts +0 -7
  62. package/lib/routes/calendar/calendar/calendar-multiget.js +0 -68
  63. package/lib/routes/calendar/calendar/calendar-query.d.ts +0 -7
  64. package/lib/routes/calendar/calendar/calendar-query.js +0 -68
  65. package/lib/routes/calendar/calendar/delete.d.ts +0 -5
  66. package/lib/routes/calendar/calendar/delete.js +0 -40
  67. package/lib/routes/calendar/calendar/eventResponse.d.ts +0 -7
  68. package/lib/routes/calendar/calendar/eventResponse.js +0 -36
  69. package/lib/routes/calendar/calendar/expand-property.d.ts +0 -7
  70. package/lib/routes/calendar/calendar/expand-property.js +0 -15
  71. package/lib/routes/calendar/calendar/get.d.ts +0 -5
  72. package/lib/routes/calendar/calendar/get.js +0 -30
  73. package/lib/routes/calendar/calendar/propfind.d.ts +0 -8
  74. package/lib/routes/calendar/calendar/propfind.js +0 -76
  75. package/lib/routes/calendar/calendar/proppatch.d.ts +0 -5
  76. package/lib/routes/calendar/calendar/proppatch.js +0 -53
  77. package/lib/routes/calendar/calendar/put.d.ts +0 -5
  78. package/lib/routes/calendar/calendar/put.js +0 -68
  79. package/lib/routes/calendar/calendar/report.d.ts +0 -5
  80. package/lib/routes/calendar/calendar/report.js +0 -39
  81. package/lib/routes/calendar/calendar/sync-collection.d.ts +0 -10
  82. package/lib/routes/calendar/calendar/sync-collection.js +0 -57
  83. package/lib/routes/calendar/calendar.d.ts +0 -3
  84. package/lib/routes/calendar/calendar.js +0 -78
  85. package/lib/routes/calendar/user/propfind.d.ts +0 -5
  86. package/lib/routes/calendar/user/propfind.js +0 -64
  87. package/lib/routes/calendar/user/proppatch.d.ts +0 -5
  88. package/lib/routes/calendar/user/proppatch.js +0 -52
  89. package/lib/routes/principal/principal.d.ts +0 -3
  90. package/lib/routes/principal/principal.js +0 -32
  91. package/lib/routes/principal/propfind.d.ts +0 -3
  92. package/lib/routes/principal/propfind.js +0 -50
  93. package/lib/routes/principal/report.d.ts +0 -3
  94. package/lib/routes/principal/report.js +0 -32
package/common/tags.js ADDED
@@ -0,0 +1,412 @@
1
+ const { buildTag, href, response, status } = require('./x-build');
2
+ const winston = require('./winston');
3
+
4
+ const dav = 'DAV:';
5
+ const cal = 'urn:ietf:params:xml:ns:caldav';
6
+ const cs = 'http://calendarserver.org/ns/';
7
+ const ical = 'http://apple.com/ns/ical/';
8
+
9
+ module.exports = function (options) {
10
+ const log = winston({ ...options, label: 'tags' });
11
+ const tags = {
12
+ [dav]: {
13
+ 'current-user-principal': {
14
+ doc: 'https://tools.ietf.org/html/rfc5397#section-3',
15
+ async resp({ ctx }) {
16
+ return {
17
+ [buildTag(dav, 'current-user-principal')]: href(
18
+ ctx.state.principalUrl
19
+ )
20
+ };
21
+ }
22
+ },
23
+ 'current-user-privilege-set': {
24
+ doc: 'https://tools.ietf.org/html/rfc3744#section-5.4',
25
+ async resp({ resource, calendar }) {
26
+ if (resource === 'calendar') {
27
+ const privileges = [{ [buildTag(dav, 'read')]: '' }];
28
+ if (!calendar.readonly) {
29
+ privileges.push(
30
+ { [buildTag(dav, 'read')]: '' },
31
+ { [buildTag(dav, 'read-acl')]: '' },
32
+ { [buildTag(dav, 'read-current-user-privilege-set')]: '' },
33
+ { [buildTag(dav, 'write')]: '' },
34
+ { [buildTag(dav, 'write-content')]: '' },
35
+ { [buildTag(dav, 'write-properties')]: '' },
36
+ { [buildTag(dav, 'bind')]: '' }, // PUT - https://tools.ietf.org/html/rfc3744#section-3.9
37
+ { [buildTag(dav, 'unbind')]: '' }, // DELETE - https://tools.ietf.org/html/rfc3744#section-3.10
38
+ { [buildTag(cal, 'read-free-busy')]: '' } // https://tools.ietf.org/html/rfc4791#section-6.1.1
39
+ );
40
+ }
41
+
42
+ return {
43
+ [buildTag(dav, 'current-user-privilege-set')]: {
44
+ [buildTag(dav, 'privilege')]: privileges
45
+ }
46
+ };
47
+ }
48
+ }
49
+ },
50
+ displayname: {
51
+ doc: 'https://tools.ietf.org/html/rfc4918#section-15.2',
52
+ async resp({ resource, ctx, calendar }) {
53
+ if (resource === 'principal') {
54
+ return {
55
+ [buildTag(dav, 'displayname')]: ctx.state.user.principalName
56
+ };
57
+ }
58
+
59
+ if (resource === 'calendar') {
60
+ return {
61
+ [buildTag(dav, 'displayname')]: calendar.name
62
+ };
63
+ }
64
+ }
65
+ },
66
+ getcontenttype: {
67
+ doc: 'https://tools.ietf.org/html/rfc2518#section-13.5',
68
+ async resp({ resource }) {
69
+ if (resource === 'calendar') {
70
+ return {
71
+ [buildTag(dav, 'getcontenttype')]:
72
+ 'text/calendar; charset=utf-8; component=VEVENT'
73
+ };
74
+ }
75
+
76
+ if (resource === 'event') {
77
+ return {
78
+ [buildTag(dav, 'getcontenttype')]:
79
+ 'text/calendar; charset=utf-8; component=VEVENT'
80
+ };
81
+ }
82
+ }
83
+ },
84
+ getetag: {
85
+ doc: 'https://tools.ietf.org/html/rfc4791#section-5.3.4',
86
+ async resp({ resource, event }) {
87
+ if (resource === 'event') {
88
+ return {
89
+ [buildTag(dav, 'getetag')]: options.data.getETag(event)
90
+ };
91
+ }
92
+ }
93
+ },
94
+ owner: {
95
+ doc: 'https://tools.ietf.org/html/rfc3744#section-5.1',
96
+ async resp({ resource, ctx }) {
97
+ if (resource === 'calendar') {
98
+ return {
99
+ [buildTag(dav, 'owner')]: href(ctx.state.principalUrl)
100
+ };
101
+ }
102
+ }
103
+ },
104
+ 'principal-collection-set': {
105
+ doc: 'https://tools.ietf.org/html/rfc3744#section-5.8',
106
+ async resp({ resource, ctx }) {
107
+ if (resource === 'principal') {
108
+ return {
109
+ [buildTag(dav, 'principal-collection-set')]: href(
110
+ ctx.state.principalRootUrl
111
+ )
112
+ };
113
+ }
114
+ }
115
+ },
116
+ 'principal-URL': {
117
+ doc: 'https://tools.ietf.org/html/rfc3744#section-4.2',
118
+ async resp({ ctx }) {
119
+ return {
120
+ [buildTag(dav, 'principal-URL')]: href(ctx.state.principalUrl)
121
+ };
122
+ }
123
+ },
124
+ 'resource-id': {
125
+ doc: 'https://tools.ietf.org/html/rfc5842#section-3.1'
126
+ },
127
+ resourcetype: {
128
+ doc: 'https://tools.ietf.org/html/rfc4791#section-4.2',
129
+ async resp({ resource }) {
130
+ if (resource === 'calCollection') {
131
+ return {
132
+ [buildTag(dav, 'resourcetype')]: {
133
+ [buildTag(dav, 'collection')]: ''
134
+ }
135
+ };
136
+ }
137
+
138
+ if (resource === 'calendar') {
139
+ return {
140
+ [buildTag(dav, 'resourcetype')]: {
141
+ [buildTag(dav, 'collection')]: '',
142
+ [buildTag(cal, 'calendar')]: ''
143
+ }
144
+ };
145
+ }
146
+ }
147
+ },
148
+ 'supported-report-set': {
149
+ doc: 'https://tools.ietf.org/html/rfc3253#section-3.1.5',
150
+ async resp({ resource }) {
151
+ if (resource === 'calCollection') {
152
+ return {
153
+ [buildTag(dav, 'supported-report-set')]: {
154
+ [buildTag(dav, 'supported-report')]: {
155
+ [buildTag(dav, 'report')]: {
156
+ [buildTag(cal, 'sync-collection')]: ''
157
+ }
158
+ }
159
+ }
160
+ };
161
+ }
162
+
163
+ if (resource === 'calendar') {
164
+ return {
165
+ [buildTag(dav, 'supported-report-set')]: {
166
+ [buildTag(dav, 'supported-report')]: [
167
+ {
168
+ [buildTag(dav, 'report')]: {
169
+ [buildTag(cal, 'calendar-query')]: ''
170
+ }
171
+ },
172
+ {
173
+ [buildTag(dav, 'report')]: {
174
+ [buildTag(cal, 'calendar-multiget')]: ''
175
+ }
176
+ },
177
+ {
178
+ [buildTag(dav, 'report')]: {
179
+ [buildTag(cal, 'sync-collection')]: ''
180
+ }
181
+ }
182
+ ]
183
+ }
184
+ };
185
+ }
186
+ }
187
+ },
188
+ 'sync-token': {
189
+ doc: 'https://tools.ietf.org/html/rfc6578#section-3',
190
+ async resp({ resource, calendar }) {
191
+ if (resource === 'calendar') {
192
+ return {
193
+ [buildTag(dav, 'sync-token')]: calendar.synctoken
194
+ };
195
+ }
196
+ }
197
+ }
198
+ },
199
+ [cal]: {
200
+ 'calendar-data': {
201
+ doc: 'https://tools.ietf.org/html/rfc4791#section-9.6',
202
+ async resp({ event, calendar }) {
203
+ const ics = await options.data.buildICS(event, calendar);
204
+ return {
205
+ [buildTag(cal, 'calendar-data')]: ics
206
+ };
207
+ }
208
+ },
209
+ 'calendar-home-set': {
210
+ doc: 'https://tools.ietf.org/html/rfc4791#section-6.2.1',
211
+ async resp({ resource, ctx }) {
212
+ if (resource === 'principal') {
213
+ return {
214
+ [buildTag(cal, 'calendar-home-set')]: href(
215
+ ctx.state.calendarHomeUrl
216
+ )
217
+ };
218
+ }
219
+ }
220
+ },
221
+ 'calendar-description': {
222
+ doc: 'https://tools.ietf.org/html/rfc4791#section-5.2.1',
223
+ async resp({ resource, ctx, calendar }) {
224
+ if (resource === 'calendar') {
225
+ return {
226
+ [buildTag(ical, 'calendar-description')]: calendar.description
227
+ };
228
+ }
229
+
230
+ if (resource === 'calendarProppatch') {
231
+ return response(ctx.url, status[403], [
232
+ {
233
+ [buildTag(cal, 'calendar-description')]: ''
234
+ }
235
+ ]);
236
+ }
237
+ }
238
+ },
239
+ 'calendar-timezone': {
240
+ doc: 'https://tools.ietf.org/html/rfc4791#section-5.2.2',
241
+ async resp({ resource, ctx, calendar }) {
242
+ if (resource === 'calendar') {
243
+ return {
244
+ [buildTag(ical, 'calendar-timezone')]: calendar.timezone
245
+ };
246
+ }
247
+
248
+ if (resource === 'calendarProppatch') {
249
+ return response(ctx.url, status[403], [
250
+ {
251
+ [buildTag(cal, 'calendar-timezone')]: ''
252
+ }
253
+ ]);
254
+ }
255
+ }
256
+ },
257
+ 'calendar-user-address-set': {
258
+ doc: 'https://tools.ietf.org/html/rfc6638#section-2.4.1'
259
+ },
260
+ 'default-alarm-vevent-date': {
261
+ doc: 'https://tools.ietf.org/id/draft-daboo-valarm-extensions-01.html#rfc.section.9',
262
+ async resp({ resource, ctx }) {
263
+ if (resource === 'calCollectionProppatch') {
264
+ return response(ctx.url, status[403], [
265
+ {
266
+ [buildTag(cal, 'default-alarm-vevent-date')]: ''
267
+ }
268
+ ]);
269
+ }
270
+ }
271
+ },
272
+ 'default-alarm-vevent-datetime': {
273
+ doc: 'https://tools.ietf.org/id/draft-daboo-valarm-extensions-01.html#rfc.section.9',
274
+ async resp({ resource, ctx }) {
275
+ if (resource === 'calCollectionProppatch') {
276
+ return response(ctx.url, status[403], [
277
+ {
278
+ [buildTag(cal, 'default-alarm-vevent-datetime')]: ''
279
+ }
280
+ ]);
281
+ }
282
+ }
283
+ },
284
+ 'schedule-inbox-URL': {
285
+ doc: 'https://tools.ietf.org/html/rfc6638#section-2.2',
286
+ async resp() {
287
+ return {
288
+ [buildTag(cal, 'schedule-inbox-URL')]: href('')
289
+ };
290
+ }
291
+ },
292
+ 'schedule-outbox-URL': {
293
+ doc: 'https://tools.ietf.org/html/rfc6638#section-2.1',
294
+ async resp() {
295
+ return {
296
+ [buildTag(cal, 'schedule-outbox-URL')]: href('')
297
+ };
298
+ }
299
+ },
300
+ 'supported-calendar-component-set': {
301
+ doc: 'https://tools.ietf.org/html/rfc4791#section-5.2.3',
302
+ async resp({ resource }) {
303
+ if (resource === 'calendar') {
304
+ return {
305
+ [buildTag(cal, 'supported-calendar-component-set')]: {
306
+ [buildTag(cal, 'comp')]: {
307
+ '@name': 'VEVENT'
308
+ }
309
+ }
310
+ };
311
+ }
312
+ }
313
+ }
314
+ },
315
+ [cs]: {
316
+ 'allowed-sharing-modes': {
317
+ doc: 'https://github.com/apple/ccs-calendarserver/blob/master/doc/Extensions/caldav-sharing.txt',
318
+ async resp({ resource }) {
319
+ if (resource === 'calendar') {
320
+ return {
321
+ [buildTag(cs, 'allowed-sharing-modes')]: ''
322
+ };
323
+ }
324
+ }
325
+ },
326
+ 'checksum-versions': {},
327
+ 'dropbox-home-URL': {},
328
+ 'email-address-set': {},
329
+ getctag: {
330
+ // DEPRECATED
331
+ doc: 'https://github.com/apple/ccs-calendarserver/blob/master/doc/Extensions/caldav-ctag.txt',
332
+ async resp({ resource, calendar }) {
333
+ if (resource === 'calendar') {
334
+ return {
335
+ [buildTag(cs, 'getctag')]: calendar.synctoken
336
+ };
337
+ }
338
+ }
339
+ },
340
+ 'notification-URL': {
341
+ doc: 'https://github.com/apple/ccs-calendarserver/blob/master/doc/Extensions/caldav-notifications.txt'
342
+ }
343
+ },
344
+ [ical]: {
345
+ 'calendar-color': {
346
+ async resp({ resource, ctx, calendar }) {
347
+ if (resource === 'calendar') {
348
+ return {
349
+ [buildTag(ical, 'calendar-color')]: calendar.color
350
+ };
351
+ }
352
+
353
+ if (resource === 'calendarProppatch') {
354
+ return response(ctx.url, status[403], [
355
+ {
356
+ [buildTag(ical, 'calendar-color')]: ''
357
+ }
358
+ ]);
359
+ }
360
+ }
361
+ },
362
+ 'calendar-order': {
363
+ async resp({ resource, ctx }) {
364
+ if (
365
+ resource === 'calCollectionProppatch' ||
366
+ resource === 'calendarProppatch'
367
+ ) {
368
+ return response(ctx.url, status[403], [
369
+ {
370
+ [buildTag(ical, 'calendar-order')]: ''
371
+ }
372
+ ]);
373
+ }
374
+ }
375
+ }
376
+ }
377
+ };
378
+ const getResponse = async ({ resource, child, ctx, calendar, event }) => {
379
+ if (!child.namespaceURI) {
380
+ return null;
381
+ }
382
+
383
+ if (!tags[child.namespaceURI]) {
384
+ log.debug(`Namespace miss: ${child.namespaceURI}`);
385
+ return null;
386
+ }
387
+
388
+ const tagAction = tags[child.namespaceURI][child.localName];
389
+
390
+ if (!tagAction) {
391
+ log.debug(`Tag miss: ${buildTag(child.namespaceURI, child.localName)}`);
392
+ return null;
393
+ }
394
+
395
+ if (!tagAction.resp) {
396
+ log.debug(
397
+ `Tag no response: ${buildTag(child.namespaceURI, child.localName)}`
398
+ );
399
+ return null;
400
+ }
401
+
402
+ return tagAction.resp({
403
+ resource,
404
+ ctx,
405
+ calendar,
406
+ event,
407
+ text: child.textContent
408
+ });
409
+ };
410
+
411
+ return { tags, getResponse };
412
+ };
@@ -0,0 +1,32 @@
1
+ const { format, createLogger, transports } = require('winston');
2
+
3
+ module.exports = function (options) {
4
+ const logger = createLogger({
5
+ level: options.logLevel || 'debug',
6
+ format: format.combine(
7
+ format.colorize(),
8
+ format.timestamp({
9
+ format: 'YYYY-MM-DD HH:mm:ss:SSS'
10
+ }),
11
+ format.label({ label: options.label }),
12
+ format.align(),
13
+ format.printf((info) => {
14
+ return `${info.timestamp} ${info.level} [${info.label}]: ${info.message}`;
15
+ })
16
+ )
17
+ });
18
+
19
+ if (options.logEnabled) {
20
+ logger.add(new transports.Console());
21
+ } else {
22
+ logger.silent = true;
23
+ }
24
+
25
+ logger.morganStream = {
26
+ write(message) {
27
+ logger.verbose(message.trim());
28
+ }
29
+ };
30
+
31
+ return logger;
32
+ };
@@ -0,0 +1,109 @@
1
+ const { create } = require('xmlbuilder2');
2
+ const mapKeys = require('lodash/mapKeys');
3
+ const { nsLookup, namespaces } = require('./xml');
4
+
5
+ module.exports = {};
6
+
7
+ const nsMap = function () {
8
+ return mapKeys(namespaces, (v, k) => {
9
+ return `@xmlns:${k}`;
10
+ });
11
+ };
12
+
13
+ function buildTag(namespaceURI, localName) {
14
+ return `${nsLookup[namespaceURI]}:${localName}`;
15
+ }
16
+
17
+ module.exports.buildTag = buildTag;
18
+
19
+ function href(url) {
20
+ return { [buildTag('DAV:', 'href')]: url };
21
+ }
22
+
23
+ module.exports.href = href;
24
+
25
+ function build(object) {
26
+ const doc = create(object, {
27
+ version: '1.0',
28
+ // eslint-disable-next-line unicorn/text-encoding-identifier-case
29
+ encoding: 'UTF-8',
30
+ noDoubleEncoding: true
31
+ });
32
+ return doc.end({ prettyPrint: true });
33
+ }
34
+
35
+ module.exports.build = build;
36
+
37
+ function multistatus(responses, other) {
38
+ const res = {
39
+ [buildTag('DAV:', 'multistatus')]: nsMap()
40
+ };
41
+ if (responses?.length) {
42
+ res[buildTag('DAV:', 'multistatus')][buildTag('DAV:', 'response')] =
43
+ responses;
44
+ }
45
+
46
+ if (other) {
47
+ res[buildTag('DAV:', 'multistatus')] = Object.assign(
48
+ res[buildTag('DAV:', 'multistatus')],
49
+ other
50
+ );
51
+ }
52
+
53
+ return res;
54
+ }
55
+
56
+ module.exports.multistatus = multistatus;
57
+
58
+ const status = {
59
+ 200: 'HTTP/1.1 200 OK',
60
+ 403: 'HTTP/1.1 403 Forbidden',
61
+ 404: 'HTTP/1.1 404 Not Found'
62
+ };
63
+ module.exports.status = status;
64
+
65
+ function response(url, status, props) {
66
+ const res = href(url);
67
+ res[buildTag('DAV:', 'propstat')] = [
68
+ {
69
+ [buildTag('DAV:', 'status')]: status
70
+ }
71
+ ];
72
+ if (props && props.length > 0) {
73
+ res[buildTag('DAV:', 'propstat')][0][buildTag('DAV:', 'prop')] =
74
+ Object.assign({}, ...props);
75
+ }
76
+
77
+ return res;
78
+ }
79
+
80
+ module.exports.response = response;
81
+
82
+ module.exports.missingPropstats = function (props) {
83
+ // eslint-disable-next-line unicorn/no-array-reduce
84
+ return props.reduce(
85
+ (res, v) => {
86
+ res[buildTag('DAV:', 'prop')][v] = '';
87
+ return res;
88
+ },
89
+ {
90
+ [buildTag('DAV:', 'status')]: status[404],
91
+ [buildTag('DAV:', 'prop')]: {}
92
+ }
93
+ );
94
+ };
95
+
96
+ module.exports.notFound = function (href) {
97
+ return build(multistatus([response(href, status[404])]));
98
+ };
99
+
100
+ /* https://tools.ietf.org/html/rfc4791#section-5.3.2.1 */
101
+ module.exports.preconditionFail = function (url, reason) {
102
+ const res = {
103
+ 'D:error': {
104
+ [buildTag('urn:ietf:params:xml:ns:caldav', reason)]: url,
105
+ ...nsMap()
106
+ }
107
+ };
108
+ return build(res);
109
+ };
package/common/xml.js ADDED
@@ -0,0 +1,36 @@
1
+ const xpath = require('xpath');
2
+
3
+ module.exports = {};
4
+
5
+ const namespaces = {
6
+ D: 'DAV:',
7
+ CAL: 'urn:ietf:params:xml:ns:caldav',
8
+ CS: 'http://calendarserver.org/ns/',
9
+ ICAL: 'http://apple.com/ns/ical/'
10
+ };
11
+ module.exports.namespaces = namespaces;
12
+
13
+ const nsLookup = {
14
+ 'DAV:': 'D',
15
+ 'urn:ietf:params:xml:ns:caldav': 'CAL',
16
+ 'http://calendarserver.org/ns/': 'CS',
17
+ 'http://apple.com/ns/ical/': 'ICAL'
18
+ };
19
+ module.exports.nsLookup = nsLookup;
20
+
21
+ const select = xpath.useNamespaces(namespaces);
22
+
23
+ function get(path, doc) {
24
+ return select(path, doc);
25
+ }
26
+
27
+ module.exports.get = get;
28
+
29
+ function getWithChildren(path, doc) {
30
+ const propNode = get(path, doc);
31
+ // eslint-disable-next-line unicorn/prefer-spread
32
+ const children = propNode[0] ? Array.from(propNode[0].childNodes) : [];
33
+ return { propNode, children };
34
+ }
35
+
36
+ module.exports.getWithChildren = getWithChildren;