Merge remote-tracking branch 'origin/main' into task-192

This commit is contained in:
Fenris Wolf 2024-11-10 17:19:58 +01:00
commit 5de4728901
9 changed files with 519 additions and 149 deletions

View file

@ -1,11 +1,11 @@
/** /**
* @author fenris * @author fenris
*/ */
type int = number; declare type int = number;
/** /**
* @author fenris * @author fenris
*/ */
type float = number; declare type float = number;
declare var process: any; declare var process: any;
declare var require: any; declare var require: any;
declare class Buffer { declare class Buffer {
@ -22,7 +22,7 @@ declare namespace lib_plankton.base {
/** /**
* @author fenris * @author fenris
*/ */
type type_pseudopointer<type_value> = { declare type type_pseudopointer<type_value> = {
value: type_value; value: type_value;
}; };
/** /**
@ -644,33 +644,11 @@ declare namespace lib_plankton.call {
fallback?: (null | ((coproduct?: type_coproduct) => type_output)); fallback?: (null | ((coproduct?: type_coproduct) => type_output));
}): type_output; }): type_output;
/** /**
* for rate_limit_check
*
* @author fenris
*/ */
type type_mana_snapshot = { function try_catch_wrap<type_value>(get_value: (() => type_value)): {
timestamp: float; value: (null | type_value);
value: float; 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<void>);
update_snapshot: ((timestamp: float, value_increment: float) => Promise<void>);
}, heft: float): Promise<{
granted: boolean;
seconds: (null | float);
}>;
} }
declare namespace lib_plankton.email { declare namespace lib_plankton.email {
/** /**
@ -2186,7 +2164,7 @@ declare namespace lib_plankton.storage.memory {
clear(): Promise<void>; clear(): Promise<void>;
write(key: any, value: any): Promise<boolean>; write(key: any, value: any): Promise<boolean>;
delete(key: any): Promise<void>; delete(key: any): Promise<void>;
read(key: any): Promise<Awaited<type_item>>; read(key: any): Promise<type_item>;
search(term: any): Promise<{ search(term: any): Promise<{
key: string; key: string;
preview: string; preview: string;
@ -3117,7 +3095,7 @@ declare namespace lib_plankton.pit {
/** /**
* @todo test * @todo test
*/ */
function to_datetime(pit: type_pit, options?: { function to_datetime(pit: type_pit, { timezone_shift, }?: {
timezone_shift?: int; timezone_shift?: int;
}): type_datetime; }): type_datetime;
/** /**
@ -3167,6 +3145,48 @@ declare namespace lib_plankton.pit {
* @todo write tests * @todo write tests
*/ */
function cest_switch_off(year: int): type_pit; 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 { declare namespace lib_plankton.ical {
/** /**
@ -3274,9 +3294,14 @@ declare namespace lib_plankton.ical {
}; };
last_modified?: type_datetime; last_modified?: type_datetime;
location?: string; location?: string;
/**
* @see https://www.rfc-editor.org/rfc/rfc5545#section-3.8.4.3
*/
organizer?: { organizer?: {
cn?: string;
value?: string; value?: string;
cn?: string;
dir?: string;
sent_by?: string;
}; };
priority?: int; priority?: int;
sequence?: int; sequence?: int;
@ -3325,8 +3350,18 @@ declare namespace lib_plankton.ical {
* @see https://icalendar.org/iCalendar-RFC-5545/ * @see https://icalendar.org/iCalendar-RFC-5545/
* @todo implement edge cases * @todo implement edge cases
*/ */
function ics_decode(ics: string, options?: { function ics_decode_multi(ics_raw: string, { ignore_unhandled_instruction_keys, from_fucked_up_wordpress, }?: {
debug?: boolean; ignore_unhandled_instruction_keys?: boolean;
from_fucked_up_wordpress?: boolean;
}): Array<type_vcalendar>;
/**
* @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; }): type_vcalendar;
/** /**
* @todo method * @todo method

View file

@ -1494,63 +1494,22 @@ var lib_plankton;
} }
call.distinguish = distinguish; 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) { function try_catch_wrap(get_value) {
if (heft > setup.capacity) { try {
return Promise.resolve({ return {
"granted": false, "value": get_value(),
"seconds": null, "error": null,
}); };
} }
else { catch (error) {
// get current value return {
const current_timestamp = (Date.now() / 1000); "value": null,
const old_snapshot_raw = (await setup.get_snapshot()); "error": error,
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,
});
}
} }
} }
call.rate_limit_check = rate_limit_check; call.try_catch_wrap = try_catch_wrap;
})(call = lib_plankton.call || (lib_plankton.call = {})); })(call = lib_plankton.call || (lib_plankton.call = {}));
})(lib_plankton || (lib_plankton = {})); })(lib_plankton || (lib_plankton = {}));
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 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 verb(n) { return function (v) { return step([n, v]); }; }
function step(op) { function step(op) {
if (f) throw new TypeError("Generator is already executing."); 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 (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]; if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) { switch (op[0]) {
@ -4900,11 +4859,16 @@ var lib_plankton;
if (options.legacy) { if (options.legacy) {
const value = args[key]; const value = args[key];
const regexp_argument = new RegExp("\\${" + key + "}", "g"); const regexp_argument = new RegExp("\\${" + key + "}", "g");
lib_plankton.log.debug("lib_plankton.string.coin", { /*
"key": key, lib_plankton.log.debug(
"regex": regexp_argument.toString(), "lib_plankton.string.coin",
"value": value, {
}); "key": key,
"regex": regexp_argument.toString(),
"value": value,
}
);
*/
str = str.replace(regexp_argument, value); str = str.replace(regexp_argument, value);
} }
} }
@ -4912,11 +4876,16 @@ var lib_plankton;
{ {
const value = args[key]; const value = args[key];
const regexp_argument = new RegExp(options.open + key + options.close, "g"); const regexp_argument = new RegExp(options.open + key + options.close, "g");
lib_plankton.log.debug("lib_plankton.string.coin", { /*
"key": key, lib_plankton.log.debug(
"regex": regexp_argument.toString(), "lib_plankton.string.coin",
"value": value, {
}); "key": key,
"regex": regexp_argument.toString(),
"value": value,
}
);
*/
str = str.replace(regexp_argument, 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 verb(n) { return function (v) { return step([n, v]); }; }
function step(op) { function step(op) {
if (f) throw new TypeError("Generator is already executing."); 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 (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]; if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) { 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 verb(n) { return function (v) { return step([n, v]); }; }
function step(op) { function step(op) {
if (f) throw new TypeError("Generator is already executing."); 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 (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]; if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) { switch (op[0]) {
@ -10678,18 +10647,15 @@ var lib_plankton;
/** /**
* @todo test * @todo test
*/ */
function to_datetime(pit, options = {}) { function to_datetime(pit, { timezone_shift = 0, } = {}) {
options = Object.assign({
"timezone_shift": 0,
}, options);
return lib_plankton.call.convey(pit, [ return lib_plankton.call.convey(pit, [
to_date_object, to_date_object,
(x) => x.getTime(), (x) => x.getTime(),
(x) => (x + ((options.timezone_shift * (60 * 60)) * 1000)), (x) => (x + ((timezone_shift * (60 * 60)) * 1000)),
(x) => new Date(x), (x) => new Date(x),
x => x.toISOString(), x => x.toISOString(),
x => ({ x => ({
"timezone_shift": options.timezone_shift, "timezone_shift": timezone_shift,
"date": { "date": {
"year": parseInt(x.slice(0, 4)), "year": parseInt(x.slice(0, 4)),
"month": parseInt(x.slice(5, 7)), "month": parseInt(x.slice(5, 7)),
@ -10986,6 +10952,182 @@ var lib_plankton;
]); ]);
} }
pit_1.cest_switch_off = cest_switch_off; 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 = {})); })(pit = lib_plankton.pit || (lib_plankton.pit = {}));
})(lib_plankton || (lib_plankton = {})); })(lib_plankton || (lib_plankton = {}));
/* /*
@ -11207,11 +11349,22 @@ var lib_plankton;
* @see https://icalendar.org/iCalendar-RFC-5545/ * @see https://icalendar.org/iCalendar-RFC-5545/
* @todo implement edge cases * @todo implement edge cases
*/ */
function ics_decode(ics, options = {}) { function ics_decode_multi(ics_raw, { ignore_unhandled_instruction_keys = false, from_fucked_up_wordpress = false, } = {}) {
options = Object.assign({
"debug": false,
}, options);
// preprocessing // 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"); const lines = ics.split("\r\n");
let content_lines = []; let content_lines = [];
let content_line_buffer = null; 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 instructions = content_lines.map((content_line) => {
const parts = content_line.split(":"); const parts = content_line.split(":");
const parts_left = parts[0].split(";"); const parts_left = parts[0].split(";");
@ -11259,16 +11418,21 @@ var lib_plankton;
.map(x => x.replace(new RegExp("\\\\,", "g"), ","))), .map(x => x.replace(new RegExp("\\\\,", "g"), ","))),
}; };
}); });
lib_plankton.log.debug("plankton.ical.ics_decode_multi.instructions", {
"instructions": instructions,
});
// core // core
let state = { let state = {
"label": enum_decode_state_label.expect_vcalendar_begin, "label": enum_decode_state_label.expect_vcalendar_begin,
"vcalendar_list": [],
"vcalendar": null, "vcalendar": null,
"vevent": null, "vevent": null,
}; };
instructions.forEach((instruction) => { instructions.forEach((instruction) => {
if (options.debug) { lib_plankton.log.debug("plankton.ical.ics_decode_multi.step", {
console.info(JSON.stringify({ "instruction": instruction, "state": state }, undefined, "\t")); "state": state,
} "current_instruction": instruction,
});
switch (state.label) { switch (state.label) {
default: { default: {
throw (new Error("unhandled state label: " + state.label)); throw (new Error("unhandled state label: " + state.label));
@ -11277,18 +11441,27 @@ var lib_plankton;
case enum_decode_state_label.expect_vcalendar_begin: { case enum_decode_state_label.expect_vcalendar_begin: {
switch (instruction.command) { switch (instruction.command) {
default: { 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)); throw (new Error("unexpected instruction key: " + instruction.command));
break; break;
} }
case "BEGIN": { case "BEGIN": {
switch (instruction.value[0]) { switch (instruction.value[0]) {
default: { 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])); throw (new Error("unexpected instruction value: " + instruction.value[0]));
break; break;
} }
case "VCALENDAR": { case "VCALENDAR": {
state = { state = {
"label": enum_decode_state_label.expect_vcalendar_property, "label": enum_decode_state_label.expect_vcalendar_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": { "vcalendar": {
"version": "", "version": "",
"prodid": "", "prodid": "",
@ -11309,6 +11482,7 @@ var lib_plankton;
case "VERSION": { case "VERSION": {
state = { state = {
"label": enum_decode_state_label.expect_vcalendar_property, "label": enum_decode_state_label.expect_vcalendar_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["version", instruction.value[0]]])), "vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["version", instruction.value[0]]])),
"vevent": state.vevent, "vevent": state.vevent,
}; };
@ -11317,6 +11491,7 @@ var lib_plankton;
case "PRODID": { case "PRODID": {
state = { state = {
"label": enum_decode_state_label.expect_vcalendar_property, "label": enum_decode_state_label.expect_vcalendar_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["prodid", instruction.value[0]]])), "vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["prodid", instruction.value[0]]])),
"vevent": state.vevent, "vevent": state.vevent,
}; };
@ -11325,25 +11500,46 @@ var lib_plankton;
case "METHOD": { case "METHOD": {
state = { state = {
"label": enum_decode_state_label.expect_vcalendar_property, "label": enum_decode_state_label.expect_vcalendar_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["method", instruction.value[0]]])), "vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["method", instruction.value[0]]])),
"vevent": state.vevent, "vevent": state.vevent,
}; };
break; 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": { case "BEGIN": {
const object = instruction.value[0]; const object = instruction.value[0];
switch (object) { switch (object) {
default: { 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)); throw (new Error("unhandled object: " + object));
break; break;
} }
case "VCALENDAR": { 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)); throw (new Error("unexpected object: " + object));
break; break;
} }
case "VEVENT": { case "VEVENT": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": { "vevent": {
"uid": "", "uid": "",
@ -11362,13 +11558,19 @@ var lib_plankton;
const object = instruction.value[0]; const object = instruction.value[0];
switch (object) { switch (object) {
default: { 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)); throw (new Error("unhandled object: " + object));
break; break;
} }
case "VCALENDAR": { case "VCALENDAR": {
state = { state = {
"label": enum_decode_state_label.done, "label": enum_decode_state_label.expect_vcalendar_begin,
"vcalendar": state.vcalendar, "vcalendar_list": state.vcalendar_list.concat([state.vcalendar]),
"vcalendar": null,
"vevent": state.vevent, "vevent": state.vevent,
}; };
break; break;
@ -11382,6 +11584,10 @@ var lib_plankton;
const value = instruction.value.join(";"); const value = instruction.value.join(";");
state = { state = {
"label": enum_decode_state_label.expect_vcalendar_property, "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, { "vcalendar": Object.assign(state.vcalendar, {
"x_props": Object.assign((state.vcalendar.x_props ?? {}), Object.fromEntries([[key, value]])) "x_props": Object.assign((state.vcalendar.x_props ?? {}), Object.fromEntries([[key, value]]))
}), }),
@ -11389,8 +11595,16 @@ var lib_plankton;
}; };
} }
else { else {
console.info({ "instruction": instruction, "state": state }); lib_plankton.log.warning("plankton.ical.ics_decode.error.vcalendar.unhandled_instruction_key", {
throw (new Error("unhandled instruction key: " + instruction.command)); "state": state,
"instruction": instruction,
});
if (ignore_unhandled_instruction_keys) {
// do nothing
}
else {
throw (new Error("unhandled instruction key: " + instruction.command));
}
} }
break; break;
} }
@ -11402,6 +11616,7 @@ var lib_plankton;
case "UID": { case "UID": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([["uid", instruction.value[0]]])), "vevent": Object.assign(state.vevent, Object.fromEntries([["uid", instruction.value[0]]])),
}; };
@ -11410,14 +11625,20 @@ var lib_plankton;
case "DTSTART": { case "DTSTART": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([ "vevent": Object.assign(state.vevent, Object.fromEntries([
[ [
"dtstart", "dtstart",
{ Object.assign({
"tzid": instruction.parameters["tzid"],
"value": datetime_decode(instruction.value[0]), "value": datetime_decode(instruction.value[0]),
} }, (("tzid" in instruction.parameters)
?
{
"tzid": instruction.parameters["tzid"],
}
:
{}))
] ]
])), ])),
}; };
@ -11426,14 +11647,20 @@ var lib_plankton;
case "DTEND": { case "DTEND": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([ "vevent": Object.assign(state.vevent, Object.fromEntries([
[ [
"dtend", "dtend",
{ Object.assign({
"tzid": instruction.parameters["tzid"],
"value": datetime_decode(instruction.value[0]), "value": datetime_decode(instruction.value[0]),
} }, (("tzid" in instruction.parameters)
?
{
"tzid": instruction.parameters["tzid"],
}
:
{}))
] ]
])), ])),
}; };
@ -11442,6 +11669,7 @@ var lib_plankton;
case "DTSTAMP": { case "DTSTAMP": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([ "vevent": Object.assign(state.vevent, Object.fromEntries([
[ [
@ -11455,6 +11683,7 @@ var lib_plankton;
case "SEQUENCE": { case "SEQUENCE": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([["sequence", parseInt(instruction.value[0])]])), "vevent": Object.assign(state.vevent, Object.fromEntries([["sequence", parseInt(instruction.value[0])]])),
}; };
@ -11463,6 +11692,7 @@ var lib_plankton;
case "TRANSP": { case "TRANSP": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([["transp", transp_decode(instruction.value[0])]])), "vevent": Object.assign(state.vevent, Object.fromEntries([["transp", transp_decode(instruction.value[0])]])),
}; };
@ -11471,6 +11701,7 @@ var lib_plankton;
case "SUMMARY": { case "SUMMARY": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([["summary", instruction.value[0]]])), "vevent": Object.assign(state.vevent, Object.fromEntries([["summary", instruction.value[0]]])),
}; };
@ -11479,6 +11710,7 @@ var lib_plankton;
case "CLASS": { case "CLASS": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([["class", class_decode(instruction.value[0])]])), "vevent": Object.assign(state.vevent, Object.fromEntries([["class", class_decode(instruction.value[0])]])),
}; };
@ -11487,6 +11719,7 @@ var lib_plankton;
case "STATUS": { case "STATUS": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([["status", event_status_decode(instruction.value[0])]])), "vevent": Object.assign(state.vevent, Object.fromEntries([["status", event_status_decode(instruction.value[0])]])),
}; };
@ -11495,6 +11728,7 @@ var lib_plankton;
case "DESCRIPTION": { case "DESCRIPTION": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([["description", instruction.value[0]]])), "vevent": Object.assign(state.vevent, Object.fromEntries([["description", instruction.value[0]]])),
}; };
@ -11503,6 +11737,7 @@ var lib_plankton;
case "CATEGORIES": { case "CATEGORIES": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([["categories", instruction.value[0].split(",")]])), "vevent": Object.assign(state.vevent, Object.fromEntries([["categories", instruction.value[0].split(",")]])),
}; };
@ -11511,6 +11746,7 @@ var lib_plankton;
case "CREATED": { case "CREATED": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([ "vevent": Object.assign(state.vevent, Object.fromEntries([
[ [
@ -11526,6 +11762,7 @@ var lib_plankton;
case "LOCATION": { case "LOCATION": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([["location", instruction.value[0]]])), "vevent": Object.assign(state.vevent, Object.fromEntries([["location", instruction.value[0]]])),
}; };
@ -11534,6 +11771,7 @@ var lib_plankton;
case "URL": { case "URL": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([["url", instruction.value[0]]])), "vevent": Object.assign(state.vevent, Object.fromEntries([["url", instruction.value[0]]])),
}; };
@ -11542,6 +11780,7 @@ var lib_plankton;
case "LAST-MODIFIED": { case "LAST-MODIFIED": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([ "vevent": Object.assign(state.vevent, Object.fromEntries([
[ [
@ -11557,23 +11796,58 @@ var lib_plankton;
case "ATTENDEE": { case "ATTENDEE": {
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, Object.fromEntries([["attendee", instruction.value[0]]])), "vevent": Object.assign(state.vevent, Object.fromEntries([["attendee", instruction.value[0]]])),
}; };
break; 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": { case "BEGIN": {
const object = instruction.value[0]; const object = instruction.value[0];
switch (object) { switch (object) {
default: { 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)); throw (new Error("unhandled object: " + object));
break; break;
} }
case "VCALENDAR": { 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)); throw (new Error("unexpected object: " + object));
break; break;
} }
case "VEVENT": { 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)); throw (new Error("unexpected object: " + object));
break; break;
} }
@ -11584,12 +11858,18 @@ var lib_plankton;
const object = instruction.value[0]; const object = instruction.value[0];
switch (object) { switch (object) {
default: { 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)); throw (new Error("unhandled value: " + object));
break; break;
} }
case "VEVENT": { case "VEVENT": {
state = { state = {
"label": enum_decode_state_label.expect_vcalendar_property, "label": enum_decode_state_label.expect_vcalendar_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": Object.assign(state.vcalendar, { "vcalendar": Object.assign(state.vcalendar, {
"vevents": state.vcalendar.vevents.concat([state.vevent]), "vevents": state.vcalendar.vevents.concat([state.vevent]),
}), }),
@ -11606,6 +11886,7 @@ var lib_plankton;
const value = instruction.value.join(";"); const value = instruction.value.join(";");
state = { state = {
"label": enum_decode_state_label.expect_vevent_property, "label": enum_decode_state_label.expect_vevent_property,
"vcalendar_list": state.vcalendar_list,
"vcalendar": state.vcalendar, "vcalendar": state.vcalendar,
"vevent": Object.assign(state.vevent, { "vevent": Object.assign(state.vevent, {
"x_props": Object.assign((state.vevent.x_props ?? {}), Object.fromEntries([[key, value]])) "x_props": Object.assign((state.vevent.x_props ?? {}), Object.fromEntries([[key, value]]))
@ -11613,8 +11894,16 @@ var lib_plankton;
}; };
} }
else { else {
console.info({ "instruction": instruction, "state": state }); lib_plankton.log.warning("plankton.ical.ics_decode.error.vevent.unhandled_instruction_key", {
throw (new Error("unhandled instruction key: " + instruction.command)); "state": state,
"instruction": instruction,
});
if (ignore_unhandled_instruction_keys) {
// do nothing
}
else {
throw (new Error("unhandled instruction key: " + instruction.command));
}
} }
break; break;
} }
@ -11622,13 +11911,39 @@ var lib_plankton;
break; break;
} }
case enum_decode_state_label.done: { 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")); throw (new Error("end expected"));
break; 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; ical.ics_decode = ics_decode;
/** /**
@ -15497,7 +15812,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
function verb(n) { return function (v) { return step([n, v]); }; } function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) { function step(op) {
if (f) throw new TypeError("Generator is already executing."); 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 (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]; if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) { switch (op[0]) {

View file

@ -33,6 +33,7 @@ namespace _zeitbild.api
data : { data : {
url : string; url : string;
read_only : boolean; read_only : boolean;
from_fucked_up_wordpress : boolean;
}; };
} }
); );
@ -79,6 +80,7 @@ namespace _zeitbild.api
"data": { "data": {
"url": stuff.input.resource.data.url, "url": stuff.input.resource.data.url,
"read_only": stuff.input.resource.data.read_only, "read_only": stuff.input.resource.data.read_only,
"from_fucked_up_wordpress": stuff.input.resource.data.from_fucked_up_wordpress,
} }
}; };
break; break;

View file

@ -160,27 +160,31 @@ namespace _zeitbild.api
user_id user_id
) )
.then( .then(
(data) => Promise.resolve({ (data) => Promise.resolve(
"status_code": 200, {
"data": ( "status_code": 200,
data "data": (
.map( data
(entry) => ({ .map(
"calendar_id": entry.calendar_id, (entry) => ({
"calendar_name": entry.calendar_name, "calendar_id": entry.calendar_id,
"access_level": _zeitbild.api.access_level_encode(entry.access_level), "calendar_name": entry.calendar_name,
"event_id": entry.event_id, "access_level": _zeitbild.api.access_level_encode(entry.access_level),
"event_object": entry.event_object, "event_id": entry.event_id,
}) "event_object": entry.event_object,
) })
), )
}) ),
}
)
) )
.catch( .catch(
(reason) => Promise.resolve({ (reason) => Promise.resolve(
"status_code": 403, {
"data": String(reason), "status_code": 403,
}) "data": String(reason),
}
)
) )
); );
} }

View file

@ -5,7 +5,7 @@ namespace _zeitbild.database
/** /**
*/ */
const _compatible_revisions : Array<string> = [ const _compatible_revisions : Array<string> = [
"r3", "r4",
]; ];

View file

@ -40,6 +40,7 @@ type type_data = {
data : { data : {
url : string; url : string;
read_only : boolean; read_only : boolean;
from_fucked_up_wordpress ?: boolean;
}; };
} }
); );
@ -109,6 +110,7 @@ async function data_init(
"data": { "data": {
"url": calendar_raw.resource.data.url, "url": calendar_raw.resource.data.url,
"read_only": calendar_raw.resource.data.read_only, "read_only": calendar_raw.resource.data.read_only,
"from_fucked_up_wordpress": (calendar_raw.resource.data.from_fucked_up_wordpress ?? false),
} }
}; };
resource_id = await _zeitbild.service.resource.add( resource_id = await _zeitbild.service.resource.add(

View file

@ -366,6 +366,7 @@ namespace _zeitbild.repository.resource
"data": { "data": {
"url": dataset_extra_caldav["url"], "url": dataset_extra_caldav["url"],
"read_only": dataset_extra_caldav["read_only"], "read_only": dataset_extra_caldav["read_only"],
"from_fucked_up_wordpress": dataset_extra_caldav["from_fucked_up_wordpress"],
} }
} }
); );
@ -409,6 +410,7 @@ namespace _zeitbild.repository.resource
{ {
"url": resource_object.data.url, "url": resource_object.data.url,
"read_only": resource_object.data.read_only, "read_only": resource_object.data.read_only,
"from_fucked_up_wordpress": resource_object.data.from_fucked_up_wordpress,
} }
); );
const resource_id : _zeitbild.type_resource_id = await get_resource_core_store().create( const resource_id : _zeitbild.type_resource_id = await get_resource_core_store().create(

View file

@ -348,11 +348,20 @@ namespace _zeitbild.service.calendar
{ {
} }
); );
const vcalendar : lib_plankton.ical.type_vcalendar = lib_plankton.ical.ics_decode( const ics_raw : string = http_response.body.toString();
http_response.body.toString(), const vcalendar_list : Array<lib_plankton.ical.type_vcalendar> = lib_plankton.ical.ics_decode_multi(
ics_raw,
{ {
"ignore_unhandled_instruction_keys": resource_object.data.from_fucked_up_wordpress,
"from_fucked_up_wordpress": resource_object.data.from_fucked_up_wordpress,
} }
); );
const vcalendar : lib_plankton.ical.type_vcalendar = {
// required
"version": vcalendar_list[0].version,
"prodid": vcalendar_list[0].prodid,
"vevents": vcalendar_list.map(x => x.vevents).reduce((x, y) => x.concat(y), []),
};
return Promise.resolve( return Promise.resolve(
vcalendar.vevents vcalendar.vevents
.map( .map(

View file

@ -86,6 +86,7 @@ namespace _zeitbild
data : { data : {
url : string; url : string;
read_only : boolean; read_only : boolean;
from_fucked_up_wordpress : boolean;
}; };
} }
); );