diff --git a/lib/plankton/plankton.d.ts b/lib/plankton/plankton.d.ts index ade56fc..98a26b1 100644 --- a/lib/plankton/plankton.d.ts +++ b/lib/plankton/plankton.d.ts @@ -1,11 +1,11 @@ /** * @author fenris */ -type int = number; +declare type int = number; /** * @author fenris */ -type float = number; +declare type float = number; declare var process: any; declare var require: any; declare class Buffer { @@ -22,7 +22,7 @@ declare namespace lib_plankton.base { /** * @author fenris */ -type type_pseudopointer = { +declare type type_pseudopointer = { value: type_value; }; /** @@ -644,33 +644,11 @@ declare namespace lib_plankton.call { fallback?: (null | ((coproduct?: type_coproduct) => type_output)); }): type_output; /** - * for rate_limit_check - * - * @author fenris */ - type type_mana_snapshot = { - timestamp: float; - value: float; + function try_catch_wrap(get_value: (() => type_value)): { + value: (null | type_value); + error: (null | any); }; - /** - * rate limiting algorithm, based on the idea of mana (magic power) in video games: - * - an actor has a fixed mana capacity, i.e. the maximum amount of available power - * - an actor has a fixed rate of mana regeneration, i.e. how fast the power is filled up (linear growth) - * - an action has a defined mana heft, i.e. how much power is required and deducted in order to execute it - * - mana states are represented by snapshots, i.e. the amount of power at a certain point in time - * - * @author fenris - */ - function rate_limit_check(setup: { - capacity: float; - regeneration_rate: float; - get_snapshot: (() => Promise<(null | type_mana_snapshot)>); - set_snapshot: ((snapshot: type_mana_snapshot) => Promise); - update_snapshot: ((timestamp: float, value_increment: float) => Promise); - }, heft: float): Promise<{ - granted: boolean; - seconds: (null | float); - }>; } declare namespace lib_plankton.email { /** @@ -2186,7 +2164,7 @@ declare namespace lib_plankton.storage.memory { clear(): Promise; write(key: any, value: any): Promise; delete(key: any): Promise; - read(key: any): Promise>; + read(key: any): Promise; search(term: any): Promise<{ key: string; preview: string; @@ -3117,7 +3095,7 @@ declare namespace lib_plankton.pit { /** * @todo test */ - function to_datetime(pit: type_pit, options?: { + function to_datetime(pit: type_pit, { timezone_shift, }?: { timezone_shift?: int; }): type_datetime; /** @@ -3167,6 +3145,48 @@ declare namespace lib_plankton.pit { * @todo write tests */ function cest_switch_off(year: int): type_pit; + /** + */ + function timezone_shift_ce(pit: type_pit): int; + /** + * [convenience] + */ + function to_datetime_ce(pit: type_pit): type_datetime; + /** + */ + function datetime_translate(datetime: type_datetime, timezone_shift: int): type_datetime; + /** + * [convenience] + */ + function datetime_translate_ce(datetime: type_datetime): type_datetime; + /** + */ + function timezone_shift_format(timezone_shift: int): string; + /** + */ + function date_format(date: type_date): string; + /** + */ + function time_format(time: type_time, { show_seconds, }?: { + show_seconds?: boolean; + }): string; + /** + * @todo show timezone + */ + function datetime_format(datetime: (null | type_datetime), { timezone_indicator, show_timezone, adjust_to_ce, omit_date, }?: { + timezone_indicator?: string; + show_timezone?: boolean; + adjust_to_ce?: boolean; + omit_date?: boolean; + }): string; + /** + */ + function timespan_format(from: type_datetime, to: (null | type_datetime), { timezone_indicator, show_timezone, adjust_to_ce, omit_date, }?: { + timezone_indicator?: string; + show_timezone?: boolean; + adjust_to_ce?: boolean; + omit_date?: boolean; + }): string; } declare namespace lib_plankton.ical { /** @@ -3274,9 +3294,14 @@ declare namespace lib_plankton.ical { }; last_modified?: type_datetime; location?: string; + /** + * @see https://www.rfc-editor.org/rfc/rfc5545#section-3.8.4.3 + */ organizer?: { - cn?: string; value?: string; + cn?: string; + dir?: string; + sent_by?: string; }; priority?: int; sequence?: int; @@ -3325,8 +3350,18 @@ declare namespace lib_plankton.ical { * @see https://icalendar.org/iCalendar-RFC-5545/ * @todo implement edge cases */ - function ics_decode(ics: string, options?: { - debug?: boolean; + function ics_decode_multi(ics_raw: string, { ignore_unhandled_instruction_keys, from_fucked_up_wordpress, }?: { + ignore_unhandled_instruction_keys?: boolean; + from_fucked_up_wordpress?: boolean; + }): Array; + /** + * @see https://www.rfc-editor.org/rfc/rfc5545 + * @see https://icalendar.org/iCalendar-RFC-5545/ + * @todo implement edge cases + */ + function ics_decode(ics: string, { ignore_unhandled_instruction_keys, from_fucked_up_wordpress, }?: { + ignore_unhandled_instruction_keys?: boolean; + from_fucked_up_wordpress?: boolean; }): type_vcalendar; /** * @todo method diff --git a/lib/plankton/plankton.js b/lib/plankton/plankton.js index 3ccab96..35ee966 100644 --- a/lib/plankton/plankton.js +++ b/lib/plankton/plankton.js @@ -1494,63 +1494,22 @@ var lib_plankton; } call.distinguish = distinguish; /** - * rate limiting algorithm, based on the idea of mana (magic power) in video games: - * - an actor has a fixed mana capacity, i.e. the maximum amount of available power - * - an actor has a fixed rate of mana regeneration, i.e. how fast the power is filled up (linear growth) - * - an action has a defined mana heft, i.e. how much power is required and deducted in order to execute it - * - mana states are represented by snapshots, i.e. the amount of power at a certain point in time - * - * @author fenris */ - async function rate_limit_check(setup, heft) { - if (heft > setup.capacity) { - return Promise.resolve({ - "granted": false, - "seconds": null, - }); + function try_catch_wrap(get_value) { + try { + return { + "value": get_value(), + "error": null, + }; } - else { - // get current value - const current_timestamp = (Date.now() / 1000); - const old_snapshot_raw = (await setup.get_snapshot()); - const old_snapshot = (old_snapshot_raw - ?? - { "timestamp": current_timestamp, "value": setup.capacity }); - const seconds_passed = (current_timestamp - old_snapshot.timestamp); - const current_value = Math.min(setup.capacity, (old_snapshot.value - + - (setup.regeneration_rate - * - seconds_passed))); - // analyze - if (current_value < heft) { - // too less - const seconds_needed = ((setup.regeneration_rate <= 0) - ? null - : ((heft - old_snapshot.value) / setup.regeneration_rate)); - return Promise.resolve({ - "granted": false, - "seconds": ((seconds_needed === null) ? null : (seconds_needed - seconds_passed)), - }); - } - else { - // enough -> update snapshot - const new_value = (current_value - heft); - // set_snapshot - if (old_snapshot_raw === null) { - await setup.set_snapshot({ "timestamp": current_timestamp, "value": new_value }); - } - else { - await setup.update_snapshot(current_timestamp, (new_value - old_snapshot.value)); - } - return Promise.resolve({ - "granted": true, - "seconds": 0, - }); - } + catch (error) { + return { + "value": null, + "error": error, + }; } } - call.rate_limit_check = rate_limit_check; + call.try_catch_wrap = try_catch_wrap; })(call = lib_plankton.call || (lib_plankton.call = {})); })(lib_plankton || (lib_plankton = {})); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { @@ -1568,7 +1527,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { + while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { @@ -4900,11 +4859,16 @@ var lib_plankton; if (options.legacy) { const value = args[key]; const regexp_argument = new RegExp("\\${" + key + "}", "g"); - lib_plankton.log.debug("lib_plankton.string.coin", { - "key": key, - "regex": regexp_argument.toString(), - "value": value, - }); + /* + lib_plankton.log.debug( + "lib_plankton.string.coin", + { + "key": key, + "regex": regexp_argument.toString(), + "value": value, + } + ); + */ str = str.replace(regexp_argument, value); } } @@ -4912,11 +4876,16 @@ var lib_plankton; { const value = args[key]; const regexp_argument = new RegExp(options.open + key + options.close, "g"); - lib_plankton.log.debug("lib_plankton.string.coin", { - "key": key, - "regex": regexp_argument.toString(), - "value": value, - }); + /* + lib_plankton.log.debug( + "lib_plankton.string.coin", + { + "key": key, + "regex": regexp_argument.toString(), + "value": value, + } + ); + */ str = str.replace(regexp_argument, value); } }); @@ -6564,7 +6533,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { + while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { @@ -10169,7 +10138,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { + while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { @@ -10678,18 +10647,15 @@ var lib_plankton; /** * @todo test */ - function to_datetime(pit, options = {}) { - options = Object.assign({ - "timezone_shift": 0, - }, options); + function to_datetime(pit, { timezone_shift = 0, } = {}) { return lib_plankton.call.convey(pit, [ to_date_object, (x) => x.getTime(), - (x) => (x + ((options.timezone_shift * (60 * 60)) * 1000)), + (x) => (x + ((timezone_shift * (60 * 60)) * 1000)), (x) => new Date(x), x => x.toISOString(), x => ({ - "timezone_shift": options.timezone_shift, + "timezone_shift": timezone_shift, "date": { "year": parseInt(x.slice(0, 4)), "month": parseInt(x.slice(5, 7)), @@ -10986,6 +10952,182 @@ var lib_plankton; ]); } pit_1.cest_switch_off = cest_switch_off; + /** + */ + function timezone_shift_ce(pit) { + const year = to_datetime(pit).date.year; + return (is_between(pit, cest_switch_on(year), cest_switch_off(year)) + ? +2 + : +1); + } + pit_1.timezone_shift_ce = timezone_shift_ce; + /** + * [convenience] + */ + function to_datetime_ce(pit) { + return to_datetime(pit, { + "timezone_shift": timezone_shift_ce(pit), + }); + } + pit_1.to_datetime_ce = to_datetime_ce; + /** + */ + function datetime_translate(datetime, timezone_shift) { + if (datetime.timezone_shift === timezone_shift) { + return datetime; + } + else { + const pit = from_datetime(datetime); + const pit_shifted = shift_hour(pit, (timezone_shift - datetime.timezone_shift)); + const datetime_shifted = to_datetime(pit_shifted); + return { + "timezone_shift": timezone_shift, + "date": datetime_shifted.date, + "time": ((datetime.time === null) + ? + null + : datetime_shifted.time) + }; + } + } + pit_1.datetime_translate = datetime_translate; + /** + * [convenience] + */ + function datetime_translate_ce(datetime) { + return datetime_translate(datetime, timezone_shift_ce(from_datetime(datetime))); + } + pit_1.datetime_translate_ce = datetime_translate_ce; + /** + */ + function timezone_shift_format(timezone_shift) { + return lib_plankton.string.coin("{{sign}}{{value}}", { + "sign": ((timezone_shift === 0) + ? + " " + : + ((timezone_shift > 0) + ? + "+" + : + "-")), + "value": Math.abs(timezone_shift).toFixed(0), + }); + } + pit_1.timezone_shift_format = timezone_shift_format; + /** + */ + function date_format(date) { + return lib_plankton.string.coin("{{year}}-{{month}}-{{day}}", { + "year": date.year.toFixed(0).padStart(4, "0"), + "month": date.month.toFixed(0).padStart(2, "0"), + "day": date.day.toFixed(0).padStart(2, "0"), + }); + } + pit_1.date_format = date_format; + /** + */ + function time_format(time, { show_seconds = false, } = {}) { + return lib_plankton.string.coin((show_seconds + ? + "{{hour}}:{{minute}}:{{seconds}}" + : + "{{hour}}:{{minute}}"), { + "hour": time.hour.toFixed(0).padStart(2, "0"), + "minute": time.minute.toFixed(0).padStart(2, "0"), + "second": time.second.toFixed(0).padStart(2, "0"), + }); + } + pit_1.time_format = time_format; + /** + * @todo show timezone + */ + function datetime_format(datetime, { timezone_indicator = "", show_timezone = false, adjust_to_ce = false, omit_date = false, } = {}) { + if (datetime === null) { + return "-"; + } + else { + const datetime_adjusted = (adjust_to_ce + ? + to_datetime_ce(from_datetime(datetime)) + : + datetime); + return lib_plankton.string.coin("{{macro_date_and_time}}{{macro_timezone}}", { + "macro_date_and_time": ([ + // date + (omit_date + ? + null + : + date_format(datetime_adjusted.date)), + // time + ((datetime_adjusted.time === null) + ? + null + : + time_format(datetime_adjusted.time)), + ] + .filter(x => (x !== null)) + .join(", ")), + "macro_timezone": (show_timezone + ? + lib_plankton.string.coin(" [{{timezone_indicator}}{{timezone_value}}]", { + "timezone_indicator": timezone_indicator, + "timezone_value": timezone_shift_format(datetime_adjusted.timezone_shift), + }) + : + ""), + }); + } + } + pit_1.datetime_format = datetime_format; + /** + */ + function timespan_format(from, to, { timezone_indicator = "", show_timezone = false, adjust_to_ce = false, omit_date = false, } = {}) { + const from_adjusted = (adjust_to_ce + ? + datetime_translate_ce(from) + : + from); + const to_adjusted = ((to === null) + ? + null + : + (adjust_to_ce + ? + datetime_translate_ce(to) + : + to)); + return lib_plankton.string.coin("{{from}}{{to_macro}}{{macro_timezone}}", { + "from": datetime_format(from_adjusted, { + "show_timezone": false, + "adjust_to_ce": false, + "omit_date": omit_date, + }), + "to_macro": ((to_adjusted === null) + ? + "" + : + lib_plankton.string.coin(" - {{to}}", { + "to": datetime_format(to_adjusted, { + "omit_date": ((from_adjusted.date.year === to_adjusted.date.year) + && + (from_adjusted.date.month === to_adjusted.date.month) + && + (from_adjusted.date.day === to_adjusted.date.day)) + }), + })), + "macro_timezone": (show_timezone + ? + lib_plankton.string.coin(" [{{timezone_indicator}}{{timezone_value}}]", { + "timezone_indicator": timezone_indicator, + "timezone_value": timezone_shift_format(from_adjusted.timezone_shift), + }) + : + ""), + }); + } + pit_1.timespan_format = timespan_format; })(pit = lib_plankton.pit || (lib_plankton.pit = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -11207,11 +11349,22 @@ var lib_plankton; * @see https://icalendar.org/iCalendar-RFC-5545/ * @todo implement edge cases */ - function ics_decode(ics, options = {}) { - options = Object.assign({ - "debug": false, - }, options); + function ics_decode_multi(ics_raw, { ignore_unhandled_instruction_keys = false, from_fucked_up_wordpress = false, } = {}) { // preprocessing + let ics; + if (from_fucked_up_wordpress) { + ics = (ics_raw + .replace(new RegExp("\r", "g"), "") + .replace(new RegExp("END:VCALENDAR", "g"), "") + .replace(new RegExp("\n", "g"), "\r\n") + .replace(new RegExp("END:VEVENT", "g"), "END:VEVENT\r\nEND:VCALENDAR")); + lib_plankton.log.debug("plankton.ical.ics_decode.preprocessing.wordpress_fuckup", { + "result": ics, + }); + } + else { + ics = ics_raw; + } const lines = ics.split("\r\n"); let content_lines = []; let content_line_buffer = null; @@ -11248,6 +11401,12 @@ var lib_plankton; } } }); + if (content_line_buffer === null) { + // do nothing + } + else { + content_lines.push(content_line_buffer); + } const instructions = content_lines.map((content_line) => { const parts = content_line.split(":"); const parts_left = parts[0].split(";"); @@ -11259,16 +11418,21 @@ var lib_plankton; .map(x => x.replace(new RegExp("\\\\,", "g"), ","))), }; }); + lib_plankton.log.debug("plankton.ical.ics_decode_multi.instructions", { + "instructions": instructions, + }); // core let state = { "label": enum_decode_state_label.expect_vcalendar_begin, + "vcalendar_list": [], "vcalendar": null, "vevent": null, }; instructions.forEach((instruction) => { - if (options.debug) { - console.info(JSON.stringify({ "instruction": instruction, "state": state }, undefined, "\t")); - } + lib_plankton.log.debug("plankton.ical.ics_decode_multi.step", { + "state": state, + "current_instruction": instruction, + }); switch (state.label) { default: { throw (new Error("unhandled state label: " + state.label)); @@ -11277,18 +11441,27 @@ var lib_plankton; case enum_decode_state_label.expect_vcalendar_begin: { switch (instruction.command) { default: { + lib_plankton.log.error("plankton.ical.ics_decode.error.vcalendar.unexpected_instruction_key", { + "state": state, + "instruction": instruction, + }); throw (new Error("unexpected instruction key: " + instruction.command)); break; } case "BEGIN": { switch (instruction.value[0]) { default: { + lib_plankton.log.error("plankton.ical.ics_decode.error.vcalendar.unexpected_instruction_value", { + "state": state, + "instruction": instruction, + }); throw (new Error("unexpected instruction value: " + instruction.value[0])); break; } case "VCALENDAR": { state = { "label": enum_decode_state_label.expect_vcalendar_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": { "version": "", "prodid": "", @@ -11309,6 +11482,7 @@ var lib_plankton; case "VERSION": { state = { "label": enum_decode_state_label.expect_vcalendar_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["version", instruction.value[0]]])), "vevent": state.vevent, }; @@ -11317,6 +11491,7 @@ var lib_plankton; case "PRODID": { state = { "label": enum_decode_state_label.expect_vcalendar_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["prodid", instruction.value[0]]])), "vevent": state.vevent, }; @@ -11325,25 +11500,46 @@ var lib_plankton; case "METHOD": { state = { "label": enum_decode_state_label.expect_vcalendar_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["method", instruction.value[0]]])), "vevent": state.vevent, }; break; } + case "CALSCALE": { + state = { + "label": enum_decode_state_label.expect_vcalendar_property, + "vcalendar_list": state.vcalendar_list, + "vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["calscale", instruction.value[0]]])), + "vevent": state.vevent, + }; + break; + } case "BEGIN": { const object = instruction.value[0]; switch (object) { default: { + lib_plankton.log.error("plankton.ical.ics_decode.error.vcalendar.unhandled_object", { + "state": state, + "instruction": instruction, + "object": object, + }); throw (new Error("unhandled object: " + object)); break; } case "VCALENDAR": { + lib_plankton.log.error("plankton.ical.ics_decode.error.vcalendar.unexpected_object", { + "state": state, + "instruction": instruction, + "object": object, + }); throw (new Error("unexpected object: " + object)); break; } case "VEVENT": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": { "uid": "", @@ -11362,13 +11558,19 @@ var lib_plankton; const object = instruction.value[0]; switch (object) { default: { + lib_plankton.log.error("plankton.ical.ics_decode.error.vcalendar.unhandled_object", { + "state": state, + "instruction": instruction, + "object": object, + }); throw (new Error("unhandled object: " + object)); break; } case "VCALENDAR": { state = { - "label": enum_decode_state_label.done, - "vcalendar": state.vcalendar, + "label": enum_decode_state_label.expect_vcalendar_begin, + "vcalendar_list": state.vcalendar_list.concat([state.vcalendar]), + "vcalendar": null, "vevent": state.vevent, }; break; @@ -11382,6 +11584,10 @@ var lib_plankton; const value = instruction.value.join(";"); state = { "label": enum_decode_state_label.expect_vcalendar_property, + "vcalendar_list": state.vcalendar_list, + /** + * @todo not sure; state.vcalendar might be null + */ "vcalendar": Object.assign(state.vcalendar, { "x_props": Object.assign((state.vcalendar.x_props ?? {}), Object.fromEntries([[key, value]])) }), @@ -11389,8 +11595,16 @@ var lib_plankton; }; } else { - console.info({ "instruction": instruction, "state": state }); - throw (new Error("unhandled instruction key: " + instruction.command)); + lib_plankton.log.warning("plankton.ical.ics_decode.error.vcalendar.unhandled_instruction_key", { + "state": state, + "instruction": instruction, + }); + if (ignore_unhandled_instruction_keys) { + // do nothing + } + else { + throw (new Error("unhandled instruction key: " + instruction.command)); + } } break; } @@ -11402,6 +11616,7 @@ var lib_plankton; case "UID": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([["uid", instruction.value[0]]])), }; @@ -11410,14 +11625,20 @@ var lib_plankton; case "DTSTART": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([ [ "dtstart", - { - "tzid": instruction.parameters["tzid"], + Object.assign({ "value": datetime_decode(instruction.value[0]), - } + }, (("tzid" in instruction.parameters) + ? + { + "tzid": instruction.parameters["tzid"], + } + : + {})) ] ])), }; @@ -11426,14 +11647,20 @@ var lib_plankton; case "DTEND": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([ [ "dtend", - { - "tzid": instruction.parameters["tzid"], + Object.assign({ "value": datetime_decode(instruction.value[0]), - } + }, (("tzid" in instruction.parameters) + ? + { + "tzid": instruction.parameters["tzid"], + } + : + {})) ] ])), }; @@ -11442,6 +11669,7 @@ var lib_plankton; case "DTSTAMP": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([ [ @@ -11455,6 +11683,7 @@ var lib_plankton; case "SEQUENCE": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([["sequence", parseInt(instruction.value[0])]])), }; @@ -11463,6 +11692,7 @@ var lib_plankton; case "TRANSP": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([["transp", transp_decode(instruction.value[0])]])), }; @@ -11471,6 +11701,7 @@ var lib_plankton; case "SUMMARY": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([["summary", instruction.value[0]]])), }; @@ -11479,6 +11710,7 @@ var lib_plankton; case "CLASS": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([["class", class_decode(instruction.value[0])]])), }; @@ -11487,6 +11719,7 @@ var lib_plankton; case "STATUS": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([["status", event_status_decode(instruction.value[0])]])), }; @@ -11495,6 +11728,7 @@ var lib_plankton; case "DESCRIPTION": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([["description", instruction.value[0]]])), }; @@ -11503,6 +11737,7 @@ var lib_plankton; case "CATEGORIES": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([["categories", instruction.value[0].split(",")]])), }; @@ -11511,6 +11746,7 @@ var lib_plankton; case "CREATED": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([ [ @@ -11526,6 +11762,7 @@ var lib_plankton; case "LOCATION": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([["location", instruction.value[0]]])), }; @@ -11534,6 +11771,7 @@ var lib_plankton; case "URL": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([["url", instruction.value[0]]])), }; @@ -11542,6 +11780,7 @@ var lib_plankton; case "LAST-MODIFIED": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([ [ @@ -11557,23 +11796,58 @@ var lib_plankton; case "ATTENDEE": { state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, Object.fromEntries([["attendee", instruction.value[0]]])), }; break; } + case "ORGANIZER": { + state = { + "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, + "vcalendar": state.vcalendar, + "vevent": Object.assign(state.vevent, Object.fromEntries([ + [ + "organizer", + { + /** + * @todo parameters + */ + "value": instruction.value[0], + } + ] + ])), + }; + break; + } case "BEGIN": { const object = instruction.value[0]; switch (object) { default: { + lib_plankton.log.error("plankton.ical.ics_decode.error.vevent.unhandled_object", { + "state": state, + "instruction": instruction, + "object": object, + }); throw (new Error("unhandled object: " + object)); break; } case "VCALENDAR": { + lib_plankton.log.error("plankton.ical.ics_decode.error.vevent.unexpected_object", { + "state": state, + "instruction": instruction, + "object": object, + }); throw (new Error("unexpected object: " + object)); break; } case "VEVENT": { + lib_plankton.log.error("plankton.ical.ics_decode.error.vevent.unexpected_object", { + "state": state, + "instruction": instruction, + "object": object, + }); throw (new Error("unexpected object: " + object)); break; } @@ -11584,12 +11858,18 @@ var lib_plankton; const object = instruction.value[0]; switch (object) { default: { + lib_plankton.log.error("plankton.ical.ics_decode.error.vevent.unhandled_value", { + "state": state, + "instruction": instruction, + "value": object, + }); throw (new Error("unhandled value: " + object)); break; } case "VEVENT": { state = { "label": enum_decode_state_label.expect_vcalendar_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": Object.assign(state.vcalendar, { "vevents": state.vcalendar.vevents.concat([state.vevent]), }), @@ -11606,6 +11886,7 @@ var lib_plankton; const value = instruction.value.join(";"); state = { "label": enum_decode_state_label.expect_vevent_property, + "vcalendar_list": state.vcalendar_list, "vcalendar": state.vcalendar, "vevent": Object.assign(state.vevent, { "x_props": Object.assign((state.vevent.x_props ?? {}), Object.fromEntries([[key, value]])) @@ -11613,8 +11894,16 @@ var lib_plankton; }; } else { - console.info({ "instruction": instruction, "state": state }); - throw (new Error("unhandled instruction key: " + instruction.command)); + lib_plankton.log.warning("plankton.ical.ics_decode.error.vevent.unhandled_instruction_key", { + "state": state, + "instruction": instruction, + }); + if (ignore_unhandled_instruction_keys) { + // do nothing + } + else { + throw (new Error("unhandled instruction key: " + instruction.command)); + } } break; } @@ -11622,13 +11911,39 @@ var lib_plankton; break; } case enum_decode_state_label.done: { - console.info({ "instruction": instruction, "state": state }); + lib_plankton.log.error("plankton.ical.ics_decode.error.end_expected", { + "state": state, + "instruction": instruction, + }); throw (new Error("end expected")); break; } } }); - return state.vcalendar; + return state.vcalendar_list; + } + ical.ics_decode_multi = ics_decode_multi; + /** + * @see https://www.rfc-editor.org/rfc/rfc5545 + * @see https://icalendar.org/iCalendar-RFC-5545/ + * @todo implement edge cases + */ + function ics_decode(ics, { ignore_unhandled_instruction_keys = false, from_fucked_up_wordpress = false, } = {}) { + const vcalendar_list = ics_decode_multi(ics, { + "ignore_unhandled_instruction_keys": ignore_unhandled_instruction_keys, + "from_fucked_up_wordpress": from_fucked_up_wordpress, + }); + if (vcalendar_list.length < 1) { + throw (new Error("no calendar data found")); + } + else { + if (vcalendar_list.length > 1) { + throw (new Error("ambiguous calendar data found")); + } + else { + return vcalendar_list[0]; + } + } } ical.ics_decode = ics_decode; /** @@ -14484,7 +14799,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { + while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) {