Repository: adamgibbons/ics
Pull request #212 (2022-03-12 08:39:32)

Remove 00 like default second value
Remove the default second value because when you send an event request and an event cancel in the same second, Google Calendar omits event cancel.


Patch:
diff --git a/README.md b/README.md
index 70f1440..8be3154 100644
--- a/README.md
+++ b/README.md
@@ -13,10 +13,13 @@ The [iCalendar](http://tools.ietf.org/html/rfc5545) generator
 
 ## Example Usage
 
+#### In node / CommonJS
+
 1) Create an iCalendar event:
 
 ```javascript
 const ics = require('ics')
+// or, in ESM: import * as ics from 'ics'
 
 const event = {
   start: [2018, 5, 30, 6, 30],
@@ -50,7 +53,7 @@ ics.createEvent(event, (error, value) => {
   // METHOD:PUBLISH
   // X-PUBLISHED-TTL:PT1H
   // BEGIN:VEVENT
-  // UID:d9e5e080-d25e-11e8-806a-e73a41d3e47b
+  // UID:S8h0Vj7mTB74p9vt5pQzJ
   // SUMMARY:Bolder Boulder
   // DTSTAMP:20181017T204900Z
   // DTSTART:20180530T043000Z
@@ -119,14 +122,14 @@ console.log(value)
 // CALSCALE:GREGORIAN
 // PRODID:adamgibbons/ics
 // BEGIN:VEVENT
-// UID:3c6d44e8-79a7-428d-acac-9586c9e06e5c
+// UID:mPfHOGi_sif_xO493Mgi6
 // SUMMARY:Lunch
 // DTSTAMP:20180210T093900Z
 // DTSTART:20180115T191500Z
 // DURATION:PT45M
 // END:VEVENT
 // BEGIN:VEVENT
-// UID:253cc897-fc26-4f25-9a01-b6bb57fa174d
+// UID:ho-KcKyhNaQVDqJCcGfXD
 // SUMMARY:Dinner
 // DTSTAMP:20180210T093900Z
 // DTSTART:20180115T191500Z
@@ -193,6 +196,43 @@ console.log(ics.createEvents(events))
 // END:VCALENDAR
 
 ```
+
+#### Using ESModules & in the browser
+
+```javascript
+import { createEvent} from 'ics';
+
+const event = {
+  ...
+}
+
+async function handleDownload() {
+  const filename = 'ExampleEvent.ics'
+  const file = await new Promise((resolve, reject) => {
+    createEvent(event, (error, value) => {
+      if (error) {
+        reject(error)
+      }
+      
+      resolve(new File([value], filename, { type: 'plain/text' }))
+    })
+  })
+  const url = URL.createObjectURL(file);
+  
+  // trying to assign the file URL to a window could cause cross-site
+  // issues so this is a workaround using HTML5
+  const anchor = document.createElement('a');
+  anchor.href = url;
+  anchor.download = filename;
+  
+  document.body.appendChild(anchor);
+  anchor.click();
+  document.body.removeChild(anchor);
+  
+  URL.revokeObjectURL(url);
+}
+```
+
 ## API
 
 ### `createEvent(attributes[, callback])`
@@ -223,12 +263,12 @@ The following properties are accepted:
 | geo   | Geographic coordinates (lat/lon) | `{ lat: 38.9072, lon: 77.0369 }`
 | url           | URL associated with event | `'http://www.mountainsunpub.com/'`
 | status        | Three statuses are allowed: `TENTATIVE`, `CONFIRMED`, `CANCELLED` | `CONFIRMED`
-| organizer     | Person organizing the event | `{ name: 'Adam Gibbons', email: 'adam@example.com', dir: 'https://linkedin.com/in/adamgibbons' }`
+| organizer     | Person organizing the event | `{ name: 'Adam Gibbons', email: 'adam@example.com', dir: 'https://linkedin.com/in/adamgibbons', sentBy: 'test@example.com' }`
 | attendees     | Persons invited to the event | `[{ name: 'Mo', email: 'mo@foo.com', rsvp: true }, { name: 'Bo', email: 'bo@bar.biz', dir: 'https://twitter.com/bo1234', partstat: 'ACCEPTED', role: 'REQ-PARTICIPANT' }]`
 | categories    | Categories associated with the event | `['hacknight', 'stout month']`
 | alarms        | Alerts that can be set to trigger before, during, or after the event. The following `attach` properties work on Mac OS: Basso, Blow, Bottle, Frog, Funk, Glass, Hero, Morse, Ping, Pop, Purr, Sousumi, Submarine, Tink | `{ action: 'display', description: 'Reminder', trigger: [2000, 1, 4, 18, 30] }` OR `{ action: 'display', description: 'Reminder', trigger: { hours: 2, minutes: 30, before: true } }` OR `{ action: 'display', description: 'Reminder', trigger: { hours: 2, minutes: 30, before: false }` OR `{ action: 'audio', description: 'Reminder', trigger: { hours: 2, minutes: 30, before: true }, repeat: 2, attachType: 'VALUE=URI', attach: 'Glass' }`
 | productId     | Product which created ics, `PRODID` field | `'adamgibbons/ics'`
-| uid           | Universal unique id for event, produced by default with `uuid/v1`.  **Warning:** This value must be **globally unique**.  It is recommended that it follow the [RFC 822 addr-spec](https://www.w3.org/Protocols/rfc822/) (i.e. `localpart@domain`).  Including the `@domain` half is a good way to ensure uniqueness. | `'28021620-be61-11e7-be87-5f3ab42f0785'`
+| uid           | Universal unique id for event, produced by default with `nanoid`.  **Warning:** This value must be **globally unique**.  It is recommended that it follow the [RFC 822 addr-spec](https://www.w3.org/Protocols/rfc822/) (i.e. `localpart@domain`).  Including the `@domain` half is a good way to ensure uniqueness. | `'LZfXLFzPPR4NNrgjlWDxn'`
 | method        | This property defines the iCalendar object method associated with the calendar object. When used in a MIME message entity, the value of this property MUST be the same as the Content-Type "method" parameter value.  If either the "METHOD" property or the Content-Type "method" parameter is specified, then the other MUST also be specified. | `PUBLISH`
 | recurrenceRule        | A recurrence rule, commonly referred to as an RRULE, defines the repeat pattern or rule for to-dos, journal entries and events. If specified, RRULE can be used to compute the recurrence set (the complete set of recurrence instances in a calendar component). You can use a generator like this [one](https://www.textmagic.com/free-tools/rrule-generator) | `FREQ=DAILY`
 | sequence      | For sending an update for an event (with the same uid), defines the revision sequence number. | `2`
diff --git a/index.d.ts b/index.d.ts
index 6658201..809a995 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -55,6 +55,9 @@ export type classificationType = 'PUBLIC' | 'PRIVATE' | 'CONFIDENTIAL' | string;
 
 export type Alarm = {
   action?: ActionType;
+  description?: string;
+  summary?: string;
+  duration?: DurationObject;
   trigger?: DurationObject; // @todo DateArray | DurationObject;
   repeat?: number;
   attachType?: string;
@@ -79,7 +82,9 @@ export type EventAttributes = {
   status?: EventStatus;
   busyStatus?: 'FREE' | 'BUSY' | 'TENTATIVE' | 'OOF';
   
-  organizer?: Person;
+  organizer?: Person & {
+    sentBy?: string;
+  };
   attendees?: Attendee[];
   
   categories?: string[];
diff --git a/package-lock.json b/package-lock.json
index 0c7b943..714d375 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "ics",
-  "version": "2.34.0",
+  "version": "2.42.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -1297,9 +1297,9 @@
       }
     },
     "balanced-match": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true
     },
     "base": {
@@ -1684,7 +1684,7 @@
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
       "dev": true
     },
     "concat-stream": {
@@ -1788,9 +1788,9 @@
       "dev": true
     },
     "decode-uri-component": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
+      "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
       "dev": true
     },
     "deep-eql": {
@@ -2997,13 +2997,10 @@
       "dev": true
     },
     "json5": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
-      "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
-      "dev": true,
-      "requires": {
-        "minimist": "^1.2.5"
-      }
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true
     },
     "just-debounce": {
       "version": "1.1.0",
@@ -3287,20 +3284,14 @@
       }
     },
     "minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
       "dev": true,
       "requires": {
         "brace-expansion": "^1.1.7"
       }
     },
-    "minimist": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
-      "dev": true
-    },
     "mixin-deep": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
@@ -3312,32 +3303,32 @@
       }
     },
     "mocha": {
-      "version": "9.1.3",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz",
-      "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==",
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz",
+      "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==",
       "dev": true,
       "requires": {
         "@ungap/promise-all-settled": "1.1.2",
         "ansi-colors": "4.1.1",
         "browser-stdout": "1.3.1",
-        "chokidar": "3.5.2",
-        "debug": "4.3.2",
+        "chokidar": "3.5.3",
+        "debug": "4.3.3",
         "diff": "5.0.0",
         "escape-string-regexp": "4.0.0",
         "find-up": "5.0.0",
-        "glob": "7.1.7",
+        "glob": "7.2.0",
         "growl": "1.10.5",
         "he": "1.2.0",
         "js-yaml": "4.1.0",
         "log-symbols": "4.1.0",
-        "minimatch": "3.0.4",
+        "minimatch": "4.2.1",
         "ms": "2.1.3",
-        "nanoid": "3.1.25",
+        "nanoid": "3.3.1",
         "serialize-javascript": "6.0.0",
         "strip-json-comments": "3.1.1",
         "supports-color": "8.1.1",
         "which": "2.0.2",
-        "workerpool": "6.1.5",
+        "workerpool": "6.2.0",
         "yargs": "16.2.0",
         "yargs-parser": "20.2.4",
         "yargs-unparser": "2.0.0"
@@ -3365,9 +3356,9 @@
           }
         },
         "anymatch": {
-          "version": "3.1.2",
-          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
-          "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+          "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
           "dev": true,
           "requires": {
             "normalize-path": "^3.0.0",
@@ -3390,9 +3381,9 @@
           }
         },
         "chokidar": {
-          "version": "3.5.2",
-          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
-          "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+          "version": "3.5.3",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+          "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
           "dev": true,
           "requires": {
             "anymatch": "~3.1.2",
@@ -3432,9 +3423,9 @@
           "dev": true
         },
         "debug": {
-          "version": "4.3.2",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
-          "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+          "version": "4.3.3",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
+          "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
           "dev": true,
           "requires": {
             "ms": "2.1.2"
@@ -3486,6 +3477,31 @@
           "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
           "dev": true
         },
+        "glob": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+          "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.4",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          },
+          "dependencies": {
+            "minimatch": {
+              "version": "3.1.2",
+              "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+              "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+              "dev": true,
+              "requires": {
+                "brace-expansion": "^1.1.7"
+              }
+            }
+          }
+        },
         "has-flag": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -3522,6 +3538,15 @@
             "p-locate": "^5.0.0"
           }
         },
+        "minimatch": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz",
+          "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        },
         "ms": {
           "version": "2.1.3",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -3529,9 +3554,9 @@
           "dev": true
         },
         "nanoid": {
-          "version": "3.1.25",
-          "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
-          "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==",
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
+          "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
           "dev": true
         },
         "p-limit": {
@@ -3679,9 +3704,9 @@
       "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA=="
     },
     "nanoid": {
-      "version": "3.1.23",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
-      "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw=="
+      "version": "3.1.31",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.31.tgz",
+      "integrity": "sha512-ZivnJm0o9bb13p2Ot5CpgC2rQdzB9Uxm/mFZweqm5eMViqOJe3PV6LU2E30SiLgheesmcPrjquqraoolONSA0A=="
     },
     "nanomatch": {
       "version": "1.2.13",
@@ -4006,15 +4031,15 @@
       }
     },
     "pathval": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
-      "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+      "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
       "dev": true
     },
     "picomatch": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
-      "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
       "dev": true
     },
     "pify": {
@@ -5221,9 +5246,9 @@
       "dev": true
     },
     "workerpool": {
-      "version": "6.1.5",
-      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz",
-      "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==",
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz",
+      "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==",
       "dev": true
     },
     "wrap-ansi": {
@@ -5306,9 +5331,9 @@
       },
       "dependencies": {
         "camelcase": {
-          "version": "6.2.1",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz",
-          "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==",
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+          "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
           "dev": true
         },
         "decamelize": {
diff --git a/package.json b/package.json
index 019aafa..c14d332 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,13 @@
 {
   "name": "ics",
-  "version": "2.35.0",
+  "version": "2.43.0",
   "description": "iCal (ics) file generator",
+  "exports": {
+    "require": "./index.js",
+    "default": "./dist/index.js"
+  },
   "main": "index.js",
+  "module": "./dist/index.js",
   "types": "index.d.ts",
   "scripts": {
     "start": "mocha --require @babel/register --watch --recursive",
diff --git a/src/pipeline/build.js b/src/pipeline/build.js
index ca59264..1039c7c 100644
--- a/src/pipeline/build.js
+++ b/src/pipeline/build.js
@@ -30,9 +30,9 @@ export default function buildEvent(attributes = {}) {
   // fill in default values where necessary
   const output = Object.assign({}, defaultAttributes, attributes);
 
-  // remove falsey values
+  // remove undefined values
   return Object.entries(output).reduce(
-      (clean, entry) => entry[1] ? Object.assign(clean, {[entry[0]]: entry[1]}) : clean,
+      (clean, entry) => typeof entry[1] !== 'undefined' ? Object.assign(clean, {[entry[0]]: entry[1]}) : clean,
       {}
   )
 }
diff --git a/src/pipeline/format.js b/src/pipeline/format.js
index 192b08c..c606cac 100644
--- a/src/pipeline/format.js
+++ b/src/pipeline/format.js
@@ -68,7 +68,7 @@ export default function formatEvent(attributes = {}) {
     }
   }
 
-  icsFormat += sequence ? (`SEQUENCE:${sequence}\r\n`) : ''
+  icsFormat += typeof sequence !== 'undefined' ? (`SEQUENCE:${sequence}\r\n`) : ''
   icsFormat += description ? (foldLine(`DESCRIPTION:${setDescription(description)}`) + '\r\n') : ''
   icsFormat += url ? (foldLine(`URL:${url}`) + '\r\n') : ''
   icsFormat += geo ? (foldLine(`GEO:${setGeolocation(geo)}`) + '\r\n') : ''
@@ -86,15 +86,13 @@ export default function formatEvent(attributes = {}) {
       icsFormat += foldLine(`ATTENDEE;${setContact(attendee)}`) + '\r\n'
     })
   }
-
+  icsFormat += recurrenceRule ? `RRULE:${recurrenceRule}\r\n` : ''
+  icsFormat += duration ? `DURATION:${formatDuration(duration)}\r\n` : ''
   if (alarms) {
     alarms.map(function (alarm) {
       icsFormat += setAlarm(alarm)
     })
   }
-
-  icsFormat += recurrenceRule ? `RRULE:${recurrenceRule}\r\n` : ''
-  icsFormat += duration ? `DURATION:${formatDuration(duration)}\r\n` : ''
   icsFormat += `END:VEVENT\r\n`
   icsFormat += `END:VCALENDAR\r\n`
 
diff --git a/src/schema/index.js b/src/schema/index.js
index 19a4f65..2c81391 100644
--- a/src/schema/index.js
+++ b/src/schema/index.js
@@ -39,7 +39,9 @@ const contactSchema = yup.object().shape({
 
 const organizerSchema = yup.object().shape({
   name: yup.string(),
-  email: yup.string().email()
+  email: yup.string().email(),
+  dir: yup.string(),
+  sentBy: yup.string()
 }).noUnknown()
 
 const alarmSchema = yup.object().shape({
diff --git a/src/utils/format-date.js b/src/utils/format-date.js
index 34dcff7..8afdf5c 100644
--- a/src/utils/format-date.js
+++ b/src/utils/format-date.js
@@ -6,7 +6,7 @@ export default function formatDate(args = [], outputType = 'utc', inputType = 'l
     return `${year}${pad(month)}${pad(date)}`
   }
 
-  let outDate = new Date(new Date().setUTCSeconds(0, 0))
+  let outDate = new Date()
   if (Array.isArray(args) && args.length > 0 && args[0]) {
     const [year, month, date, hours = 0, minutes = 0, seconds = 0] = args
     if (inputType === 'local') {
diff --git a/src/utils/set-organizer.js b/src/utils/set-organizer.js
index 90e3456..26adf00 100644
--- a/src/utils/set-organizer.js
+++ b/src/utils/set-organizer.js
@@ -1,9 +1,10 @@
-export default function setOrganizer({ name, email, dir }) {
+export default function setOrganizer({ name, email, dir, sentBy }) {
   let formattedOrganizer = ''
-  formattedOrganizer += dir ? `DIR=${dir};` : ''
+  formattedOrganizer += dir ? `DIR="${dir}";` : ''
+  formattedOrganizer += sentBy ? `SENT-BY="MAILTO:${sentBy}";` : ''
   formattedOrganizer += 'CN='
   formattedOrganizer += name || 'Organizer'
-  formattedOrganizer += email ? `:mailto:${email}` : ''
+  formattedOrganizer += email ? `:MAILTO:${email}` : ''
 
   return formattedOrganizer
 }


Test patch:
diff --git a/test/pipeline/build.spec.js b/test/pipeline/build.spec.js
index 3299f8c..cead401 100644
--- a/test/pipeline/build.spec.js
+++ b/test/pipeline/build.spec.js
@@ -47,6 +47,10 @@ describe('pipeline.build properties', () => {
       const event = buildEvent({ sequence: 5 })
       expect(event.sequence).to.equal(5)
     })
+    it('sets a sequence number when the sequence is 0', () => {
+      const event = buildEvent({ sequence: 0 })
+      expect(event.sequence).to.equal(0)
+    })
   })
   describe('start and end', () => {
     it('defaults to UTC date-time format', () => {
@@ -187,4 +191,4 @@ describe('pipeline.build properties', () => {
       expect(event.alarms).to.be.an('array')
     })
   })
-})
\ No newline at end of file
+})
diff --git a/test/pipeline/format.spec.js b/test/pipeline/format.spec.js
index d1ca25f..7aebc41 100644
--- a/test/pipeline/format.spec.js
+++ b/test/pipeline/format.spec.js
@@ -4,6 +4,7 @@ import {
   formatEvent,
   buildEvent
 } from '../../src/pipeline'
+import {foldLine} from "../../src/utils";
 
 describe('pipeline.formatEvent', () => {
   it('writes default values when no attributes passed', () => {
@@ -206,12 +207,13 @@ describe('pipeline.formatEvent', () => {
     expect(formattedEventAnyClass).to.contain('CLASS:non-standard-property')
   })
   it('writes an organizer', () => {
-    const event = formatEvent({ organizer: {
-      name: 'Adam Gibbons',
-      email: 'adam@example.com'
-    }})
-    const formattedEvent = formatEvent(event)
-    expect(event).to.contain('ORGANIZER;CN=Adam Gibbons:mailto:adam@example.com')
+    const formattedEvent = formatEvent({ organizer: {
+        name: 'Adam Gibbons',
+        email: 'adam@example.com',
+        dir: 'test-dir-value',
+        sentBy: 'test@example.com'
+      }})
+    expect(formattedEvent).to.contain(foldLine('ORGANIZER;DIR="test-dir-value";SENT-BY="MAILTO:test@example.com";CN=Adam Gibbons:MAILTO:adam@example.com'))
   })
   it('writes an alarm', () => {
     const formattedEvent = formatEvent({ alarms: [{
diff --git a/test/utils/format-date.spec.js b/test/utils/format-date.spec.js
index 9f08149..e8e09c1 100644
--- a/test/utils/format-date.spec.js
+++ b/test/utils/format-date.spec.js
@@ -8,8 +8,8 @@ import { expect } from 'chai'
 
 describe('utils.formatDate', () => {
   it('defaults to local time input and UTC time output when no type passed', () => {
-    const now = dayjs(new Date(2017, 7-1, 16, 22, 30)).utc().format('YYYYMMDDTHHmm00')
-    expect(formatDate([2017, 7, 16, 22, 30])).to.equal(now+'Z')
+    const now = dayjs(new Date(2017, 7-1, 16, 22, 30, 35)).utc().format('YYYYMMDDTHHmmss')
+    expect(formatDate([2017, 7, 16, 22, 30, 35])).to.equal(now+'Z')
   })
   it('sets a local (i.e. floating) time when specified', () => {
     expect(formatDate([1998, 6, 18, 23, 0], 'local', 'local')).to.equal('19980618T230000')
@@ -18,7 +18,7 @@ describe('utils.formatDate', () => {
     expect(formatDate([2018, 2, 11])).to.equal('20180211')
   })
   it('defaults to NOW in UTC date-time when no args passed', () => {
-    const now = dayjs().utc().format('YYYYMMDDTHHmm00') + 'Z'
+    const now = dayjs().utc().format('YYYYMMDDTHHmmss') + 'Z'
     expect(formatDate(undefined, 'utc')).to.equal(now)
   })
   it('sets a UTC date-time when passed well-formed args', () => {
@@ -26,7 +26,7 @@ describe('utils.formatDate', () => {
     expect(formatDate([2017, 1, 31], 'utc','utc')).to.equal('20170131')
   })
   it('sets a local DATE-TIME value to NOW when passed nothing', () => {
-    const now = dayjs().format('YYYYMMDDTHHmm00')
+    const now = dayjs().format('YYYYMMDDTHHmmss')
     expect(formatDate(undefined, 'local', 'local')).to.equal(now)
   })
   it('sets a local DATE-TIME value when passed args', () => {