diff --git a/lib/plankton/plankton.d.ts b/lib/plankton/plankton.d.ts index 6c8d75d..49fd537 100644 --- a/lib/plankton/plankton.d.ts +++ b/lib/plankton/plankton.d.ts @@ -229,9 +229,10 @@ declare namespace lib_plankton.base { function object_merge(core: Record, mantle: Record): Record; /** */ - function buffer_show(buffer: Buffer, { "block_size": option_block_size, "break_char": option_break_char, }?: { + function buffer_show(buffer: Buffer, { "block_size": option_block_size, "break_char": option_break_char, "render_readable_characters": render_readable_characters, }?: { block_size?: int; break_char?: string; + render_readable_characters?: boolean; }): string; } declare module lib_plankton.pod { @@ -4128,7 +4129,7 @@ declare namespace lib_plankton.server { }; /** */ - function make(handle: ((input: string, metadata?: type_metadata) => Promise), options?: { + function make(handle: ((input: string, metadata?: type_metadata) => Promise), { "host": host, "port": port, "threshold": threshold, }?: { host?: string; port?: int; threshold?: (null | float); diff --git a/lib/plankton/plankton.js b/lib/plankton/plankton.js index 1b941e6..2232892 100644 --- a/lib/plankton/plankton.js +++ b/lib/plankton/plankton.js @@ -428,18 +428,32 @@ var lib_plankton; base.object_merge = object_merge; /** */ - function buffer_show(buffer, { "block_size": option_block_size = 20, "break_char": option_break_char = "\n", } = {}) { + function buffer_show(buffer, { "block_size": option_block_size = 20, "break_char": option_break_char = "\n", "render_readable_characters": render_readable_characters = true, } = {}) { + function is_readable(code) { + return ((code > 0x20) + && + (code <= 0x7E)); + } let output = ""; let count = 0; // @ts-ignore for (const entry of buffer) { count = ((count + 1) % option_block_size); - output += ((typeof (entry) === "string") + /* + */ + const code = ((typeof (entry) === "string") ? entry.charCodeAt(0) : - entry).toString(16).toUpperCase().padStart(2, "0"); - output += ((count === 0) ? option_break_char : " "); + entry); + output += ((render_readable_characters + && + is_readable(code)) + ? + String.fromCharCode(code) + : + ("[" + code.toString(16).toUpperCase().padStart(2, "0") + "]")); + output += ((count === 0) ? option_break_char : ""); } return output; } @@ -2167,92 +2181,99 @@ var lib_plankton; function get_channel_logic(channel_description) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; switch (channel_description.kind) { - default: { - throw (new Error("unhandled log channel kind: " + channel_description.kind)); - break; - } - case "filtered": { - return lib_plankton.log.channel.filtered.logic({ - "core": get_channel_logic(channel_description.data.core), - "predicate": lib_plankton.log.channel.filtered.predicate_complex(channel_description.data.predicate.map(function (clause_raw) { return clause_raw.map(function (literal_raw) { - var _a; - return ({ - "mode": ((_a = literal_raw["mode"]) !== null && _a !== void 0 ? _a : true), - "item": lib_plankton.call.distinguish(literal_raw["item"], { - "incident": function (_a) { - var substring = _a["substring"]; - if (substring === undefined) { - throw (new Error("required parameter missing: substring")); + default: + { + throw (new Error("unhandled log channel kind: " + channel_description.kind)); + break; + } + case "filtered": + { + return lib_plankton.log.channel.filtered.logic({ + "core": get_channel_logic(channel_description.data.core), + "predicate": lib_plankton.log.channel.filtered.predicate_complex(channel_description.data.predicate.map(function (clause_raw) { return clause_raw.map(function (literal_raw) { + var _a; + return ({ + "mode": ((_a = literal_raw["mode"]) !== null && _a !== void 0 ? _a : true), + "item": lib_plankton.call.distinguish(literal_raw["item"], { + "incident": function (_a) { + var substring = _a["substring"]; + if (substring === undefined) { + throw (new Error("required parameter missing: substring")); + } + else { + return lib_plankton.log.channel.filtered.predicate_incident(substring); + } + }, + "level": function (_a) { + var threshold = _a["threshold"]; + if (threshold === undefined) { + throw (new Error("required parameter missing: threshold")); + } + else { + return lib_plankton.log.channel.filtered.predicate_level(log.level_decode(threshold)); + } + }, + "tag": function (_a) { + var value = _a["value"]; + if (value === undefined) { + throw (new Error("required parameter missing: value")); + } + else { + return lib_plankton.log.channel.filtered.predicate_tag(value); + } } - else { - return lib_plankton.log.channel.filtered.predicate_incident(substring); - } - }, - "level": function (_a) { - var threshold = _a["threshold"]; - if (threshold === undefined) { - throw (new Error("required parameter missing: threshold")); - } - else { - return lib_plankton.log.channel.filtered.predicate_level(log.level_decode(threshold)); - } - }, - "tag": function (_a) { - var value = _a["value"]; - if (value === undefined) { - throw (new Error("required parameter missing: value")); - } - else { - return lib_plankton.log.channel.filtered.predicate_tag(value); - } - } - }, { - "fallback": function () { return function (entry) { return true; }; } - }) - }); - }); })) - }); - break; - } - case "minlevel": { - return lib_plankton.log.channel.minlevel.logic({ - "core": get_channel_logic(channel_description.data.core), - "threshold": log.level_decode(channel_description.data.threshold) - }); - break; - } - case "std": { - return lib_plankton.log.channel.std.logic({ - "target": ((_b = (_a = channel_description === null || channel_description === void 0 ? void 0 : channel_description.data) === null || _a === void 0 ? void 0 : _a.target) !== null && _b !== void 0 ? _b : "stdout"), - "format": log.parse_format_definition((_c = channel_description === null || channel_description === void 0 ? void 0 : channel_description.data) === null || _c === void 0 ? void 0 : _c.format) - }); - break; - } - case "file": { - /** - * @todo exceptions on missing parameters - */ - return lib_plankton.log.channel.file.logic({ - "path": ((_e = (_d = channel_description === null || channel_description === void 0 ? void 0 : channel_description.data) === null || _d === void 0 ? void 0 : _d.path) !== null && _e !== void 0 ? _e : "log"), - "format": log.parse_format_definition((_f = channel_description === null || channel_description === void 0 ? void 0 : channel_description.data) === null || _f === void 0 ? void 0 : _f.format) - }); - break; - } - case "notify": { - return lib_plankton.log.channel.notify.logic({}); - break; - } - case "email": { - /** - * @todo exceptions on missing parameters - */ - return lib_plankton.log.channel.email.logic({ - "smtp_credentials": channel_description.data.smtp_credentials, - "sender": ((_h = (_g = channel_description.data) === null || _g === void 0 ? void 0 : _g.sender) !== null && _h !== void 0 ? _h : "plankton"), - "receivers": (_j = channel_description.data) === null || _j === void 0 ? void 0 : _j.receivers - }); - break; - } + }, { + "fallback": function () { return function (entry) { return true; }; } + }) + }); + }); })) + }); + break; + } + case "minlevel": + { + return lib_plankton.log.channel.minlevel.logic({ + "core": get_channel_logic(channel_description.data.core), + "threshold": log.level_decode(channel_description.data.threshold) + }); + break; + } + case "std": + { + return lib_plankton.log.channel.std.logic({ + "target": ((_b = (_a = channel_description === null || channel_description === void 0 ? void 0 : channel_description.data) === null || _a === void 0 ? void 0 : _a.target) !== null && _b !== void 0 ? _b : "stdout"), + "format": log.parse_format_definition((_c = channel_description === null || channel_description === void 0 ? void 0 : channel_description.data) === null || _c === void 0 ? void 0 : _c.format) + }); + break; + } + case "file": + { + /** + * @todo exceptions on missing parameters + */ + return lib_plankton.log.channel.file.logic({ + "path": ((_e = (_d = channel_description === null || channel_description === void 0 ? void 0 : channel_description.data) === null || _d === void 0 ? void 0 : _d.path) !== null && _e !== void 0 ? _e : "log"), + "format": log.parse_format_definition((_f = channel_description === null || channel_description === void 0 ? void 0 : channel_description.data) === null || _f === void 0 ? void 0 : _f.format) + }); + break; + } + case "notify": + { + return lib_plankton.log.channel.notify.logic({}); + break; + } + case "email": + { + /** + * @todo exceptions on missing parameters + */ + return lib_plankton.log.channel.email.logic({ + "smtp_credentials": channel_description.data.smtp_credentials, + "sender": ((_h = (_g = channel_description.data) === null || _g === void 0 ? void 0 : _g.sender) !== null && _h !== void 0 ? _h : "plankton"), + "receivers": (_j = channel_description.data) === null || _j === void 0 ? void 0 : _j.receivers + }); + break; + } } } log.get_channel_logic = get_channel_logic; @@ -11504,30 +11525,30 @@ var lib_plankton; /** */ function date_decode(date_encoded) { - return { + return ({ "year": parseInt(date_encoded.slice(0, 4)), "month": parseInt(date_encoded.slice(4, 6)), "day": parseInt(date_encoded.slice(6, 8)), - }; + }); } /** */ function time_decode(time_encoded) { - return { + return ({ "hour": parseInt(time_encoded.slice(0, 2)), "minute": parseInt(time_encoded.slice(2, 4)), "second": parseInt(time_encoded.slice(4, 6)), "utc": ((time_encoded.length >= 7) && (time_encoded[6] === "Z")) - }; + }); } /** */ function datetime_decode(datetime_encoded) { const parts = datetime_encoded.split("T", 2); - return { + return ({ "date": date_decode(parts[0]), "time": ((parts.length >= 2) ? time_decode(parts[1]) : null), - }; + }); } /** */ @@ -11560,11 +11581,11 @@ var lib_plankton; /** */ function class_decode(class_encoded) { - return { + return ({ "PRIVATE": ical.enum_class.private, "PUBLIC": ical.enum_class.public, "CONFIDENTIAL": ical.enum_class.confidential, - }[class_encoded]; + }[class_encoded]); } /** */ @@ -11587,11 +11608,11 @@ var lib_plankton; /** */ function event_status_decode(event_status_encoded) { - return { + return ({ "TENTATIVE": ical.enum_event_status.tentative, "CONFIRMED": ical.enum_event_status.confirmed, "CANCELLED": ical.enum_event_status.cancelled, - }[event_status_encoded]; + }[event_status_encoded]); } /** */ @@ -11610,10 +11631,10 @@ var lib_plankton; /** */ function transp_decode(transp_encoded) { - return { + return ({ "OPAQUE": ical.enum_transp.opaque, "TRANSPARENT": ical.enum_transp.transparent, - }[transp_encoded]; + }[transp_encoded]); } /** */ @@ -11663,35 +11684,37 @@ var lib_plankton; let content_lines = []; let content_line_buffer = null; lines.forEach(line => { - if (line.trim() === "") { - // do nothing - } - else { - const is_folding = ((line.length >= 2) - && - ((line[0] === " ") - || - (line[0] === "\t")) - /* - && - ! ( - (line[1] === " ") - || - (line[1] === "\t") - ) - */ - ); - if (is_folding) { - content_line_buffer += line.slice(1); + { + if (line.trim() === "") { + // do nothing } else { - if (content_line_buffer === null) { - // do nothing + const is_folding = ((line.length >= 2) + && + ((line[0] === " ") + || + (line[0] === "\t")) + /* + && + ! ( + (line[1] === " ") + || + (line[1] === "\t") + ) + */ + ); + if (is_folding) { + content_line_buffer += line.slice(1); } else { - content_lines.push(content_line_buffer); + if (content_line_buffer === null) { + // do nothing + } + else { + content_lines.push(content_line_buffer); + } + content_line_buffer = line; } - content_line_buffer = line; } } }); @@ -11702,516 +11725,563 @@ var lib_plankton; content_lines.push(content_line_buffer); } const instructions = content_lines.map((content_line) => { - const parts = content_line.split(":"); - const parts_left = parts[0].split(";"); - return { - "command": parts_left[0], - "parameters": Object.fromEntries(parts_left.slice(1).map(x => x.split("=", 2))), - "value": (parts.slice(1).join(":") - .split(";") - .map(x => x.replace(new RegExp("\\\\,", "g"), ","))), - }; + { + const parts = content_line.split(":"); + const parts_left = parts[0].split(";"); + return { + "command": parts_left[0], + "parameters": Object.fromEntries(parts_left.slice(1).map(x => x.split("=", 2))), + "value": (parts.slice(1).join(":") + .split(";") + .map(x => x.replace(new RegExp("\\\\,", "g"), ","))), + }; + } }); lib_plankton.log.debug("plankton.ical.ics_decode_multi.instructions", { "instructions": instructions, }); // core - let state = { + let state = ({ "label": enum_decode_state_label.expect_vcalendar_begin, "vcalendar_list": [], "vcalendar": null, "vevent": null, - }; + }); instructions.forEach((instruction) => { 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)); - break; - } - 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", { + default: + { + throw (new Error("unhandled state label: " + state.label)); + break; + } + 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 value: " + instruction.value[0])); + throw (new Error("unexpected instruction key: " + instruction.command)); break; } - case "VCALENDAR": { + 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": "", + "vevents": [], + }, + "vevent": null, + }); + break; + } + } + break; + } + } + break; + } + case enum_decode_state_label.expect_vcalendar_property: + { + switch (instruction.command) { + 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, + }); + break; + } + case "PRODID": + { state = { "label": enum_decode_state_label.expect_vcalendar_property, "vcalendar_list": state.vcalendar_list, - "vcalendar": { - "version": "", - "prodid": "", - "vevents": [], - }, - "vevent": null, - }; - break; - } - } - break; - } - } - break; - } - case enum_decode_state_label.expect_vcalendar_property: { - switch (instruction.command) { - 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, - }; - break; - } - 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, - }; - break; - } - 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": "", - "dtstamp": { - "date": { "year": 2000, "month": 0, "day": 0 }, - "time": { "hour": 0, "minute": 0, "second": 0, "utc": true }, - }, - }, - }; - break; - } - } - break; - } - case "END": { - 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.expect_vcalendar_begin, - "vcalendar_list": state.vcalendar_list.concat([state.vcalendar]), - "vcalendar": null, + "vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["prodid", instruction.value[0]]])), "vevent": state.vevent, }; break; } - } - break; - } - default: { - if (instruction.command.startsWith("X-")) { - const key = instruction.command.slice(2).toLowerCase(); - 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]])) - }), - "vevent": state.vevent, - }; - } - else { - 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; - } - } - break; - } - case enum_decode_state_label.expect_vevent_property: { - switch (instruction.command) { - 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]]])), - }; - break; - } - 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", - Object.assign({ - "value": datetime_decode(instruction.value[0]), - }, (("tzid" in instruction.parameters) - ? - { - "tzid": instruction.parameters["tzid"], - } - : - {})) - ] - ])), - }; - break; - } - 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", - Object.assign({ - "value": datetime_decode(instruction.value[0]), - }, (("tzid" in instruction.parameters) - ? - { - "tzid": instruction.parameters["tzid"], - } - : - {})) - ] - ])), - }; - break; - } - 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([ - [ - "dtstamp", - datetime_decode(instruction.value[0]) - ] - ])), - }; - break; - } - 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])]])), - }; - break; - } - 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])]])), - }; - break; - } - 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]]])), - }; - break; - } - 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])]])), - }; - break; - } - 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])]])), - }; - break; - } - 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]]])), - }; - break; - } - 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(",")]])), - }; - break; - } - 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([ - [ - "created", - { - "value": datetime_decode(instruction.value[0]), - } - ] - ])), - }; - break; - } - 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]]])), - }; - break; - } - 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]]])), - }; - break; - } - 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([ - [ - "last_modified", - { - "value": datetime_decode(instruction.value[0]), - } - ] - ])), - }; - break; - } - 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; - } - } - break; - } - case "END": { - 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": { + case "METHOD": + { 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]), - }), - "vevent": null, + "vcalendar": Object.assign(state.vcalendar, Object.fromEntries([["method", instruction.value[0]]])), + "vevent": state.vevent, }; break; } - } - break; - } - default: { - if (instruction.command.startsWith("X-")) { - const key = instruction.command.slice(2).toLowerCase(); - 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]])) - }), - }; - } - else { - lib_plankton.log.warning("plankton.ical.ics_decode.error.vevent.unhandled_instruction_key", { - "state": state, - "instruction": instruction, - }); - if (ignore_unhandled_instruction_keys) { - // do nothing + 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; } - else { - throw (new Error("unhandled instruction key: " + instruction.command)); + 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": "", + "dtstamp": { + "date": { "year": 2000, "month": 0, "day": 0 }, + "time": { "hour": 0, "minute": 0, "second": 0, "utc": true }, + }, + }, + }); + break; + } + } + break; + } + case "END": + { + 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.expect_vcalendar_begin, + "vcalendar_list": state.vcalendar_list.concat([state.vcalendar]), + "vcalendar": null, + "vevent": state.vevent, + }); + break; + } + } + break; + } + default: + { + if (instruction.command.startsWith("X-")) { + const key = instruction.command.slice(2).toLowerCase(); + 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]])) + }), + "vevent": state.vevent, + }); + } + else { + 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; } - } - break; } + break; + } + case enum_decode_state_label.expect_vevent_property: + { + switch (instruction.command) { + 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]]])), + }); + break; + } + 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", + Object.assign({ + "value": datetime_decode(instruction.value[0]), + }, (("tzid" in instruction.parameters) + ? + { + "tzid": instruction.parameters["tzid"], + } + : + {})) + ] + ])), + }); + break; + } + 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", + Object.assign({ + "value": datetime_decode(instruction.value[0]), + }, (("tzid" in instruction.parameters) + ? + { + "tzid": instruction.parameters["tzid"], + } + : + {})) + ] + ])), + }); + break; + } + 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([ + [ + "dtstamp", + datetime_decode(instruction.value[0]) + ] + ])), + }); + break; + } + 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])]])), + }); + break; + } + 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])]])), + }); + break; + } + 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]]])), + }); + break; + } + 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])]])), + }); + break; + } + 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])]])), + }); + break; + } + 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]]])), + }); + break; + } + 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(",")]])), + }); + break; + } + 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([ + [ + "created", + { + "value": datetime_decode(instruction.value[0]), + } + ] + ])), + }); + break; + } + 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]]])), + }); + break; + } + 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]]])), + }); + break; + } + 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([ + [ + "last_modified", + { + "value": datetime_decode(instruction.value[0]), + } + ] + ])), + }); + break; + } + 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; + } + } + break; + } + case "END": + { + 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]), + }), + "vevent": null, + }); + break; + } + } + break; + } + default: + { + if (instruction.command.startsWith("X-")) { + const key = instruction.command.slice(2).toLowerCase(); + 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]])) + }), + }); + } + else { + 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; + } + } + break; + } + case enum_decode_state_label.done: + { + lib_plankton.log.error("plankton.ical.ics_decode.error.end_expected", { + "state": state, + "instruction": instruction, + }); + throw (new Error("end expected")); + break; } - break; - } - case enum_decode_state_label.done: { - lib_plankton.log.error("plankton.ical.ics_decode.error.end_expected", { - "state": state, - "instruction": instruction, - }); - throw (new Error("end expected")); - break; - } } }); return state.vcalendar_list; @@ -14302,16 +14372,11 @@ var lib_plankton; (function (server) { /** */ - function make(handle, options = {}) { - options = Object.assign({ - "host": "::", - "port": 9999, - "threshold": 0.25, - }, options); + function make(handle, { "host": host = "::", "port": port = 9999, "threshold": threshold = 0.25, } = {}) { return { - "host": options.host, - "port": options.port, - "threshold": options.threshold, + "host": host, + "port": port, + "threshold": threshold, "handle": handle, "serverobj": undefined, }; @@ -14401,7 +14466,8 @@ var lib_plankton; } }; lib_plankton.log.info("plankton.server.client_connected"); - socket.on("data", (input_chunk_raw) => { + socket.on("data", (input_chunk_raw, x2, x3, x4, x5) => { + process.stderr.write(JSON.stringify({ x2, x3, x4, x5 }) + "\n"); lib_plankton.log.debug("plankton.server.reading_chunk", { "chunk_raw": ((input_chunk_raw instanceof Buffer) ? @@ -15744,7 +15810,7 @@ the Free Software Foundation, either version 3 of the License, or »bacterio-plankton:map« is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -3GNU Lesser General Public License for more details. +GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with »bacterio-plankton:map«. If not, see . diff --git a/misc/conf-example.json b/misc/conf-example.json index e2f68ae..dbd324f 100644 --- a/misc/conf-example.json +++ b/misc/conf-example.json @@ -1,6 +1,14 @@ { "version": 1, "log": [ + { + "kind": "file", + "data": { + "path": "/tmp/zeitbild/log.jsonl", + "threshold": "info", + "format": "jsonl_structured" + } + }, { "kind": "stdout", "data": { diff --git a/misc/data-example.json b/misc/data-example.json index 79a3f1e..a7864ed 100644 --- a/misc/data-example.json +++ b/misc/data-example.json @@ -26,6 +26,7 @@ { "id": 1, "name": "LV Lampukistan", + "hue": 0.0000, "access": { "public": true, "default_level": "view", @@ -120,6 +121,7 @@ { "id": 2, "name": "KV Zepettel-Region", + "hue": 0.3333, "access": { "public": false, "default_level": "view", @@ -191,6 +193,7 @@ { "id": 3, "name": "OV Kawanda", + "hue": 0.6667, "access": { "public": false, "default_level": "view", @@ -235,6 +238,7 @@ { "id": 4, "name": "KV Zepettel-Region | intern", + "hue": 0.8333, "access": { "public": false, "default_level": "none", diff --git a/source/api/actions/calendar_change.ts b/source/api/actions/calendar_change.ts index 65186bc..bb41388 100644 --- a/source/api/actions/calendar_change.ts +++ b/source/api/actions/calendar_change.ts @@ -33,11 +33,11 @@ namespace _zeitbild.api hue : float; access : { public : boolean; - default_level : ("none" | "view" | "edit" | "admin"); + default_level : string; attributed : Array< { user_id : int; - level : ("none" | "view" | "edit" | "admin"); + level : string; } >; }; diff --git a/source/api/actions/calendar_event_add.ts b/source/api/actions/calendar_event_add.ts index e873a8b..f349fc4 100644 --- a/source/api/actions/calendar_event_add.ts +++ b/source/api/actions/calendar_event_add.ts @@ -30,7 +30,10 @@ namespace _zeitbild.api register< _zeitbild.type_event_object, // TODO aufdröseln ( - null + { + local_resource_event_id : (null | int); + hash : string; + } | string ) @@ -88,10 +91,12 @@ namespace _zeitbild.api const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff); const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name); - if (stuff.input === null) { + if (stuff.input === null) + { return Promise.reject(new Error("impossible")); } - else { + else + { return ( _zeitbild.service.calendar.event_add( parseInt(stuff.path_parameters["calendar_id"]), @@ -99,9 +104,9 @@ namespace _zeitbild.api user_id ) .then( - () => Promise.resolve({ + (data) => Promise.resolve({ "status_code": 200, - "data": null, + "data": data, }) ) // TODO distinguish diff --git a/source/api/actions/calendar_event_change.ts b/source/api/actions/calendar_event_change.ts index 74312a2..4d1e3d0 100644 --- a/source/api/actions/calendar_event_change.ts +++ b/source/api/actions/calendar_event_change.ts @@ -88,10 +88,12 @@ namespace _zeitbild.api const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff); const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name); - if (stuff.input === null) { + if (stuff.input === null) + { return Promise.reject(new Error("impossible")); } - else { + else + { return ( _zeitbild.service.calendar.event_change( parseInt(stuff.path_parameters["calendar_id"]), @@ -100,17 +102,21 @@ namespace _zeitbild.api user_id ) .then( - () => Promise.resolve({ - "status_code": 200, - "data": null, - }) + () => Promise.resolve( + { + "status_code": 200, + "data": null, + } + ) ) // TODO distinguish .catch( - (reason) => Promise.resolve({ - "status_code": 403, - "data": String(reason), - }) + (reason) => Promise.resolve( + { + "status_code": 403, + "data": String(reason), + } + ) ) ); } diff --git a/source/api/actions/calendar_get.ts b/source/api/actions/calendar_get.ts index bde503f..6c7fa17 100644 --- a/source/api/actions/calendar_get.ts +++ b/source/api/actions/calendar_get.ts @@ -33,6 +33,7 @@ namespace _zeitbild.api name : string; hue : float; access : { + public : boolean; default_level : string; attributed : Array< { diff --git a/source/api/actions/session_status.ts b/source/api/actions/session_status.ts index 876979c..7fd8a41 100644 --- a/source/api/actions/session_status.ts +++ b/source/api/actions/session_status.ts @@ -31,6 +31,7 @@ namespace _zeitbild.api null, { logged_in : boolean; + name : (null | string); } >( rest_subject, @@ -47,9 +48,14 @@ namespace _zeitbild.api "nullable": false, "type": "boolean", }, + "name": { + "nullable": true, + "type": "string" + }, }, "required": [ "logged_in", + "name", ], }), "restriction": restriction_none, @@ -64,11 +70,19 @@ namespace _zeitbild.api ) .catch(x => Promise.resolve(null)) ); + const user_object : (null | _zeitbild.type_user_object) = ( + (user_id === null) + ? + null + : + await _zeitbild.service.user.get(user_id) + ); return Promise.resolve( { "status_code": 200, "data": { "logged_in": (user_id !== null), + "name": ((user_object === null) ? null : user_object.name), } } ); diff --git a/source/conf.ts b/source/conf.ts index dda1136..2f9953e 100644 --- a/source/conf.ts +++ b/source/conf.ts @@ -76,7 +76,51 @@ namespace _zeitbild.conf ] } } - } + }, + { + "type": "object", + "properties": { + "kind": { + "nullable": false, + "type": "string", + "enum": ["file"] + }, + "data": { + "nullable": false, + "type": "object", + "properties": { + "path": { + "nullable": false, + "type": "string" + }, + "threshold": { + "nullable": false, + "type": "string", + "enum": [ + "debug", + "info", + "notice", + "warning", + "error" + ], + "default": "info" + }, + "format": { + "nullable": false, + "type": "string", + "enum": [ + "human_readable", + "jsonl", + "jsonl_structured", + ], + "default": "human_readable", + }, + }, + "required": [ + ] + } + } + }, ] }, "default": [ @@ -291,7 +335,8 @@ namespace _zeitbild.conf "url_token", "url_userinfo", "client_id", - "client_secret" + "client_secret", + "backend_url_base" ] } }, diff --git a/source/main.ts b/source/main.ts index b588706..a1c889d 100644 --- a/source/main.ts +++ b/source/main.ts @@ -172,6 +172,46 @@ async function main( }; break; } + case "file": { + return { + "kind": "minlevel", + "data": { + "core": { + "kind": "file", + "data": { + "path": log_output.data.path, + "format": lib_plankton.call.distinguish( + { + "kind": log_output.data.format, + "data": null, + }, + { + "jsonl": () => ({ + "kind": "jsonl", + "data": { + "structured": false, + } + }), + "jsonl_structured": () => ({ + "kind": "jsonl", + "data": { + "structured": true, + } + }), + "human_readable": () => ({ + "kind": "human_readable", + "data": { + } + }), + } + ), + } + }, + "threshold": log_output.data.threshold, + } + }; + break; + } default: { throw (new Error("unhandled")); break; diff --git a/source/repositories/calendar.ts b/source/repositories/calendar.ts index 994731a..28fa046 100644 --- a/source/repositories/calendar.ts +++ b/source/repositories/calendar.ts @@ -223,6 +223,7 @@ namespace _zeitbild.repository.calendar } + /** */ export function read( diff --git a/source/services/calendar.ts b/source/services/calendar.ts index 122a8f2..2603f2d 100644 --- a/source/services/calendar.ts +++ b/source/services/calendar.ts @@ -117,7 +117,7 @@ namespace _zeitbild.service.calendar return _zeitbild.repository.calendar.overview(user_id); } - + /** */ export async function get( @@ -212,21 +212,31 @@ namespace _zeitbild.service.calendar calendar_id : _zeitbild.type_calendar_id, event_object : _zeitbild.type_event_object, user_id : _zeitbild.type_user_id - ) : Promise + ) : Promise< + { + local_resource_event_id : (null | type_local_resource_event_id); + hash : type_event_hash; + } + > { const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read( calendar_id ); - return wrap_check_access_level( + return wrap_check_access_level( calendar_object, user_id, _zeitbild.enum_access_level.edit, async () => { - /*const event_id : _zeitbild.type_local_resource_event_id = */await _zeitbild.service.resource.event_add( + const local_resource_event_id : _zeitbild.type_local_resource_event_id = await _zeitbild.service.resource.event_add( calendar_object.resource_id, event_object ); - return Promise.resolve(undefined); + return Promise.resolve( + { + "local_resource_event_id": local_resource_event_id, + "hash": get_event_hash_local(calendar_id, local_resource_event_id), + } + ); } ); } @@ -286,6 +296,45 @@ namespace _zeitbild.service.calendar } + /** + */ + function get_event_hash_local( + calendar_id : _zeitbild.type_calendar_id, + local_resource_event_id : _zeitbild.type_local_resource_event_id + ) : string + { + return lib_plankton.string.coin( + "{{calendar_id}}:{{event_id}}", + { + "calendar_id": calendar_id.toFixed(0), + "event_id": local_resource_event_id.toFixed(0), + } + ) + } + + + /** + */ + function get_event_hash_ics_feed( + calendar_id : _zeitbild.type_calendar_id, + event_object : _zeitbild.type_event_object + ) : string + { + return lib_plankton.string.coin( + "{{calendar_id}}~{{hash}}", + { + "calendar_id": calendar_id.toFixed(0), + "hash": lib_plankton.call.convey( + event_object, + [ + (x : any) => lib_plankton.json.encode(x), + (x : string) => lib_plankton.base64.encode(x), + ] + ) + } + ); + } + /** * @todo optimize by reducing the number of database queries */ @@ -326,12 +375,9 @@ namespace _zeitbild.service.calendar (event_object) => Promise.resolve( { "id": event_id, - "hash": lib_plankton.string.coin( - "{{calendar_id}}:{{event_id}}", - { - "calendar_id": calendar_id.toFixed(0), - "event_id": event_id.toFixed(0), - } + "hash": get_event_hash_local( + calendar_id, + event_id, ), "object": event_object, } @@ -457,19 +503,7 @@ namespace _zeitbild.service.calendar .map( (event) => ({ "id": null, - "hash": lib_plankton.string.coin( - "{{calendar_id}}~{{hash}}", - { - "calendar_id": calendar_id.toFixed(0), - "hash": lib_plankton.call.convey( - event, - [ - (x : any) => lib_plankton.json.encode(x), - (x : string) => lib_plankton.base64.encode(x), - ] - ) - } - ), + "hash": get_event_hash_ics_feed(calendar_id, event), "object": event, }) )