From f3d510f2ec270dc994adc41fda446336033ce8c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Fra=C3=9F?= Date: Thu, 3 Apr 2025 06:19:19 +0000 Subject: [PATCH] [upd] plankton [mod] logging --- lib/plankton/plankton.d.ts | 1394 +++--- lib/plankton/plankton.js | 4362 ++++++++++------- misc/conf.example.json | 26 +- source/api/actions/member_delete.ts | 15 +- source/api/actions/member_info.ts | 14 +- source/api/actions/member_list.ts | 14 +- source/api/actions/member_modify.ts | 12 +- .../actions/member_password_change_execute.ts | 12 +- .../member_password_change_initialize.ts | 12 +- source/api/actions/member_project.ts | 12 +- source/api/actions/member_read.ts | 12 +- source/api/actions/member_register.ts | 14 +- source/api/actions/member_summon.ts | 10 +- source/api/actions/meta_ping.ts | 10 +- source/api/actions/meta_spec.ts | 12 +- source/api/actions/session_begin.ts | 10 +- source/api/actions/session_end.ts | 13 +- source/api/base.ts | 45 +- source/api/functions.ts | 13 +- source/conf.ts | 68 +- source/main.ts | 874 ++-- source/repositories/member.ts | 4 +- tools/update-plankton | 2 +- 23 files changed, 3935 insertions(+), 3025 deletions(-) diff --git a/lib/plankton/plankton.d.ts b/lib/plankton/plankton.d.ts index 151294f..9683d1e 100644 --- a/lib/plankton/plankton.d.ts +++ b/lib/plankton/plankton.d.ts @@ -6,33 +6,11 @@ type int = number; * @author fenris */ type float = number; -/** - * @author fenris - */ -type type_date = { - year: int; - month: int; - day: int; -}; -/** - * @author fenris - */ -type type_time = { - hour: int; - minute: int; - second: int; -}; -/** - * @author fenris - */ -type type_datetimeobject = { - date: type_date; - time: type_time; -}; declare var process: any; declare var require: any; declare class Buffer { constructor(x: string, modifier?: string); + static from(x: string, encoding?: string): any; toString(modifier?: string): string; } declare namespace lib_plankton.base { @@ -249,6 +227,12 @@ 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, }?: { + block_size?: int; + break_char?: string; + }): string; } declare module lib_plankton.pod { /** @@ -299,7 +283,10 @@ declare module lib_plankton.pod { */ class class_pod { private subject; - private constructor(); + constructor(subject: type_pod); + tear(): type_pod; + static empty(): class_pod; + static filled(value: type_value): class_pod; is_empty(): boolean; is_filled(): boolean; cull(): type_value; @@ -374,36 +361,6 @@ declare namespace lib_plankton.call { */ function promise_delay(promise: type_promise, delay: int): type_promise; } -declare namespace lib_plankton.call { - /** - */ - class CancellablePromise extends Promise { - /** - */ - private cancelled; - /** - */ - private interval; - /** - */ - private subject; - /** - */ - constructor(executor: ((resolve: any, reject: any) => void)); - /** - */ - private clear; - /** - */ - then(onfulfilled?: ((value: type_result) => (type_next_resolved | PromiseLike)), onrejected?: ((reason: any) => (type_next_rejected | PromiseLike))): Promise; - /** - */ - catch(x: any): Promise; - /** - */ - cancel(): void; - } -} /** * initializer might be obsolete, since promises are reusable after having been resolved or rejected */ @@ -567,25 +524,25 @@ declare namespace lib_plankton.call { * @param {Object} args * @author fenris */ - function args2list(args: any): Array; + export function args2list(args: any): Array; /** * just the empty function; useful for some callbacks etc. * * @author fenris */ - function nothing(): void; + export function nothing(): void; /** * just the identity; useful for some callbacks etc.; defined as function instead of const for using type parameters * * @author fenris */ - function id(x: type_value): type_value; + export function id(x: type_value): type_value; /** * just the identity; useful for some callbacks etc. * * @author fenris */ - function const_(x: type_value): ((y: any) => type_value); + export function const_(x: type_value): ((y: any) => type_value); /** * composes two functions (i.e. returns a function that return the result of the successive execution of both input-functions) * @@ -593,7 +550,7 @@ declare namespace lib_plankton.call { * @param {function} function_g * @author fenris */ - function compose(function_f: ((type_x: any) => type_y), function_g: ((type_y: any) => type_z)): ((value: type_x) => type_z); + export function compose(function_f: ((type_x: any) => type_y), function_g: ((type_y: any) => type_z)): ((value: type_x) => type_z); /** * transforms a function with sequential input to a function with leveled input; example: add(2,3) = curryfy(add)(2)(3) * @@ -601,27 +558,46 @@ declare namespace lib_plankton.call { * @return {function} the currified version of the in put function * @author fenris */ - function curryfy(f: Function): Function; + export function curryfy(f: Function): Function; /** * @author fenris */ - function convey(value: any, functions: Array): any; + export function convey(value: any, functions: Array): any; + /** + */ + class class_value_wrapper { + /** + */ + private value; + /** + */ + constructor(value: type_value); + /** + */ + convey(function_: ((value: type_value) => type_value_result)): class_value_wrapper; + /** + */ + cull(): type_value; + } + /** + */ + export function wrap(value: type_value): class_value_wrapper; /** * @author fenris */ - function timeout(procedure: (() => void), delay_in_seconds: float): int; + export function timeout(procedure: (() => void), delay_in_seconds: float): int; /** * Promise version of "setTimeout" * * @author fenris */ - function defer(seconds: float, action: (() => type_result)): Promise; + export function defer(seconds: float, action: (() => type_result)): Promise; /** * a definition for a value being "defined" * * @author neuc */ - function is_def(obj: type_value, options?: { + export function is_def(obj: type_value, options?: { null_is_valid?: boolean; }): boolean; /** @@ -629,7 +605,7 @@ declare namespace lib_plankton.call { * * @author neuc */ - function def_val(value: any, default_value: any, options?: { + export function def_val(value: any, default_value: any, options?: { type?: (null | string); null_is_valid?: boolean; }): any; @@ -640,7 +616,7 @@ declare namespace lib_plankton.call { * @return {function} * @author fenris */ - function attribute(name: string): ((object: type_object) => type_attribute); + export function attribute(name: string): ((object: type_object) => type_attribute); /** * provides a method of a class as a regular function; useful for processing lists of objects * @@ -648,249 +624,184 @@ declare namespace lib_plankton.call { * @return {function} * @author fenris */ - function method(name: string): ((object: type_object) => type_output); + export function method(name: string): ((object: type_object) => type_output); /** * @author fenris */ - type type_coproduct = { + export type type_coproduct = { kind: string; data?: any; }; /** * @author fenris */ - function distinguish(coproduct: type_coproduct, handlers: Record type_output)>, options?: { + export function distinguish(coproduct: type_coproduct, handlers: Record type_output)>, options?: { fallback?: (null | ((coproduct?: type_coproduct) => type_output)); }): type_output; /** - * for rate_limit_check - * - * @author fenris */ - type type_mana_snapshot = { - timestamp: float; - value: float; + export function try_catch_wrap(get_value: (() => type_value)): { + value: (null | type_value); + error: (null | any); }; /** - * rate limiting algorithm, based on the idea of mana (magic power) in video games: - * - an actor has a fixed mana capacity, i.e. the maximum amount of available power - * - an actor has a fixed rate of mana regeneration, i.e. how fast the power is filled up (linear growth) - * - an action has a defined mana heft, i.e. how much power is required and deducted in order to execute it - * - mana states are represented by snapshots, i.e. the amount of power at a certain point in time - * - * @author fenris */ - function rate_limit_check(setup: { - capacity: float; - regeneration_rate: float; - get_snapshot: (() => Promise<(null | type_mana_snapshot)>); - set_snapshot: ((snapshot: type_mana_snapshot) => Promise); - update_snapshot: ((timestamp: float, value_increment: float) => Promise); - }, heft: float): Promise<{ - granted: boolean; - seconds: (null | float); + export function try_catch_wrap_async(get_value: (() => Promise)): Promise<{ + value: (null | type_value); + error: (null | any); }>; + export {}; +} +declare namespace lib_plankton.pair { + /** + */ + type type_pair = { + first: type_first; + second: type_second; + }; +} +declare namespace lib_plankton.pair { + /** + */ + function swap(pair: type_pair): type_pair; + /** + */ + function show(pair: type_pair, options?: { + show_first?: ((first: type_first) => string); + show_second?: ((second: type_second) => string); + }): string; } declare namespace lib_plankton.list { /** - * @desc returns a certain list of integer numbers - * @param {int} from - * @param {int} to - * @param {int} step; default: 1 - * @author fenris - */ - function range(from: int, to: int, step?: int): Array; - /** - * @desc returns a certain list of integer numbers - * @param {int} length - * @author fenris - */ - function sequence(length: int): Array; - /** - * @author fenris - */ - function from_structure(structure: { - length: int; - }): Array; - /** - * @author fenris - */ - function from_iterator(iterator: any): Array; - /** - * @author fenris - */ - function empty(list: Array): boolean; - /** - * @desc creates a list, which contains pairs of elements from the input lists (corresponding by index) - * @param {boolean} [cut] whether the result list will be as long as the shortest input list or an exception is thrown if they have different lengths; default: true - * @author fenris - */ - function zip(list1: Array, list2: Array, cut?: boolean): Array<{ - first: type_element1; - second: type_element2; - }>; - /** - * @desc checks whether two lists are equal - * @todo define common function "equals" and default predicate to - * @author fenris - */ - function equals(list1: Array, list2: Array, collate?: (element1: type_element, element2: type_element) => boolean): boolean; - /** - * @author fenris - */ - function reversed(list: Array): Array; - /** - * @author fenris - */ - function sorted(list: Array, order: (element1: type_element, element2: type_element) => int): Array; - /** - * @desc creates a list with the elements from the input list, which fulfil a certain predicate - * @author fenris - */ - function keep(list: Array, predicate: (element: type_element, index?: int) => boolean): Array; - /** - * @desc creates a list with the elements from the input list, which do not fulfil a certain predicate - * @author fenris - */ - function drop(list: Array, predicate: (element: type_element, index?: int) => boolean): Array; - /** - * @desc returns a list with no duplicates - * @author fenris - */ - function clean(list: Array, collate?: (x: type_element, y: type_element) => boolean): Array; - /** - * @author fenris - */ - function clone(list: Array): Array; - /** - * @desc creates a binary partition of the list according to a given predicate - * @author fenris */ type type_separation = { yes: Array; no: Array; }; - function separate(list: Array, predicate: (element: type_element) => boolean): type_separation; /** - * die Liste in gleich große Blöcke zerlegen - * - * @author fenris */ - function split(list: Array, chunk_size: int): Array>; - /** - * @author fenris - */ - function group(list: Array, collate?: (x: type_element, y: type_element) => boolean): Array>; - /** - * @author fenris - */ - function groups_(list: Array, collate?: (x: type_element, y: type_element) => boolean): Array>; - /** - * @author fenris - * @deprecated - */ - function groups(list: Array, sorting: (x: type_element, y: type_element) => int, hashing: (element: type_element) => type_value): Array>; - /** - * @desc searches for the first element in a list, that fulfils a given condition - * @author fenris - */ - function find(list: Array, predicate: (element: type_element) => boolean, null_if_not_found?: boolean): type_element; - /** - * @author fenris - */ - function has(list: Array, predicate: (element: type_element) => boolean): boolean; - /** - * @author fenris - */ - function contains(list: Array, element: type_element, collation?: (element1: type_element, element2: type_element) => boolean): boolean; - /** - * @desc retrieves the element and its index of the list, which has the maximum value - * @author fenris - */ - type type_result_max = { + type type_result_max = (null | { index: int; element: type_element; value: type_value; - }; - function max(list: Array, targetfunction: (element: type_element) => type_value, compare?: (value1: type_value, value2: type_value) => boolean): type_result_max; + }); +} +declare namespace lib_plankton.list { /** - * @desc retrieves the element and its index of the list, which has the mininum value - * @author fenris + * returns a certain list of integer numbers */ - function min(list: Array, targetfunction: (element: type_element) => type_value, compare?: (value1: type_value, value2: type_value) => boolean): type_result_max; + function range(from: int, to: int, options?: { + step?: int; + }): Array; /** - * @desc retrieves the element of the list, which has the maximum output for a given target-function - * @author fenris + * returns a certain list of consecutiv integer numbers, beginning with 0 */ - function maxarg(list: Array, targetfunction: (element: type_element) => type_value, compare?: (value1: type_value, value2: type_value) => boolean): type_element; + function sequence(length: int): Array; /** - * @desc retrieves the element of the list, which has the minimum output for a given target-function - * @author fenris */ - function minarg(list: Array, targetfunction: (element: type_element) => type_value, compare?: (value1: type_value, value2: type_value) => boolean): type_element; + function from_iterator(iterator: Iterator): Array; /** - * @desc implements the idea of arithmetic distribution like in "(a+b)·(c+d) = (a·c)+(a·d)+(b·c)+(b·d)" + */ + function is_empty(list: Array): boolean; + /** + * combines two lists into one + * + * @param {boolean} [options.cut] whether the result list will be as long as the shortest input list or an exception is thrown if they have different lengths; default: true + */ + function zip(list_first: Array, list_second: Array, options?: { + cut?: boolean; + }): Array>; + /** + * checks whether two lists are equal + * + * @todo define common function "equals" and default predicate to + */ + function equals(list1: Array, list2: Array, options?: { + collate_element?: ((element1: type_element, element2: type_element) => boolean); + }): boolean; + /** + * creates a list with the elements from the input list, which fulfil a certain predicate (~ filter) + */ + function keep(list: Array, predicate: ((element: type_element) => boolean)): Array; + /** + * creates a list with the elements from the input list, which do not fulfil a certain predicate (~ dual filter) + */ + function drop(list: Array, predicate: ((element: type_element) => boolean)): Array; + /** + */ + function filter_inplace(list: Array, predicate: ((element: type_element) => boolean)): void; + /** + * returns a list with no duplicates (like unix' "unique") + */ + function cleaned(list: Array, options?: { + collate_element?: ((x: type_element, y: type_element) => boolean); + }): Array; + /** + * creates a binary partition of the list according to a given predicate + */ + function separate(list: Array, predicate: ((element: type_element) => boolean)): type_separation; + /** + */ + function clone(list: Array): Array; + /** + */ + function reversed(list: Array): Array; + /** + * @todo use Array.toSorted? + */ + function sorted(list: Array, options: { + compare_element?: ((element1: type_element, element2: type_element) => boolean); + }): Array; + /** + * die Liste in gleich große Blöcke zerlegen + */ + function chop(list: Array, chunk_size: int): Array>; + /** + */ + function group(list: Array, collate_element: ((x: type_element, y: type_element) => boolean)): Array>; + /** + */ + function has(list: Array, predicate: ((element: type_element) => boolean)): boolean; + /** + * @deprecate use Array.includes or Array.some + */ + function contains(list: Array, element: type_element, options: { + collate_element?: ((element1: type_element, element2: type_element) => boolean); + }): boolean; + /** + * retrieves the element and its index of the list, which has the maximum value + */ + function max(list: Array, target_function: ((element: type_element) => type_value), options: { + compare_value: ((value1: type_value, value2: type_value) => boolean); + }): type_result_max; + /** + * retrieves the element and its index of the list, which has the mininum value + */ + function min(list: Array, target_function: (element: type_element) => type_value, options: { + compare_value: ((value1: type_value, value2: type_value) => boolean); + }): type_result_max; + /** + * implements the idea of arithmetic distribution like in "(a+b)·(c+d) = (a·c)+(a·d)+(b·c)+(b·d)" * example: distribute([[1,2],[3],[4,5,6]]) = [[1,3,4],[1,3,5],[1,3,6],[2,3,4],[2,3,5],[2,3,6]] - * @author fenris */ function distribute(lists: Array>): Array>; /** - * @desc lexicographic order - * @author fenris - * @deprecated */ - function lex(list1: Array, list2: Array, order?: (element1: type_element, element2: type_element) => int): int; - function lex_lt(list1: Array, list2: Array): boolean; - function lex_gt(list1: Array, list2: Array): boolean; - function lex_le(list1: Array, list2: Array): boolean; - function lex_ge(list1: Array, list2: Array): boolean; - function lex_eq(list1: Array, list2: Array): boolean; - function lex_ne(list1: Array, list2: Array): boolean; - /** - * @author fenris - */ - function filter_inplace(list: Array, predicate: (element: type_element, index?: int) => boolean): void; -} -/** - * @deprecated - */ -declare namespace lib_list { - type type_separation = lib_plankton.list.type_separation; - type type_result_max = lib_plankton.list.type_result_max; - const range: typeof lib_plankton.list.range; - const sequence: typeof lib_plankton.list.sequence; - const from_structure: typeof lib_plankton.list.from_structure; - const from_iterator: typeof lib_plankton.list.from_iterator; - const empty: typeof lib_plankton.list.empty; - const zip: typeof lib_plankton.list.zip; - const equals: typeof lib_plankton.list.equals; - const reversed: typeof lib_plankton.list.reversed; - const sorted: typeof lib_plankton.list.sorted; - const keep: typeof lib_plankton.list.keep; - const drop: typeof lib_plankton.list.drop; - const clean: typeof lib_plankton.list.clean; - const clone: typeof lib_plankton.list.clone; - const separate: typeof lib_plankton.list.separate; - const split: typeof lib_plankton.list.split; - const group: typeof lib_plankton.list.group; - const groups_: typeof lib_plankton.list.groups_; - const groups: typeof lib_plankton.list.groups; - const find: typeof lib_plankton.list.find; - const has: typeof lib_plankton.list.has; - const contains: typeof lib_plankton.list.contains; - const max: typeof lib_plankton.list.max; - const min: typeof lib_plankton.list.min; - const maxarg: typeof lib_plankton.list.maxarg; - const minarg: typeof lib_plankton.list.minarg; - const distribute: typeof lib_plankton.list.distribute; - const lex: typeof lib_plankton.list.lex; - const lex_lt: typeof lib_plankton.list.lex_lt; - const lex_gt: typeof lib_plankton.list.lex_gt; - const lex_le: typeof lib_plankton.list.lex_le; - const lex_ge: typeof lib_plankton.list.lex_ge; - const lex_eq: typeof lib_plankton.list.lex_eq; - const lex_ne: typeof lib_plankton.list.lex_ne; - const filter_inplace: typeof lib_plankton.list.filter_inplace; + function contrast(list_left: Array, extract_key_left: ((left: type_left) => string), list_right: Array, extract_key_right: ((right: type_right) => string)): { + both: Array<{ + key: string; + left: type_left; + right: type_right; + }>; + only_left: Array<{ + key: string; + left: type_left; + }>; + only_right: Array<{ + key: string; + right: type_right; + }>; + }; } declare namespace lib_plankton.file { /** @@ -943,181 +854,311 @@ declare namespace lib_plankton.log { warning = 3, error = 4 } - /** - */ - function level_order(level1: enum_level, level2: enum_level): boolean; - /** - */ - function level_show(level: enum_level): string; /** */ type type_entry = { level: enum_level; incident: string; - details: Record; + tags: Array; + details: any; }; -} -/** - * @deprecated - * @todo remove - */ -declare namespace lib_plankton.log { - function level_push(level: int): void; - function level_pop(): void; - function indent_push(indent: int): void; - function indent_pop(): void; - function indent_inc(): void; - function indent_dec(): void; /** - * @author fenris */ - function write({ "message": message, "type": type, "prefix": prefix, "level": level, "indent": indent, }: { - message?: string; - type?: string; - prefix?: string; - level?: int; - indent?: int; - }): void; + type type_channel_description = lib_plankton.call.type_coproduct; + /** + */ + type type_channel_logic = { + send: ((entry: type_entry) => void); + }; + /** + */ + type type_logger_data = Array; + /** + */ + type type_logger_logic = Array; + /** + */ + type type_format_definition = ({ + kind: "jsonl"; + data: { + structured: boolean; + }; + } | { + kind: "human_readable"; + data: {}; + }); } declare namespace lib_plankton.log { /** */ - abstract class class_channel { - /** - */ - abstract add(entry: type_entry): void; - } + function level_order(level1: enum_level, level2: enum_level): boolean; + /** + */ + function level_show(level: enum_level, { "abbreviated": option_abbreviated, }?: { + abbreviated?: boolean; + }): string; + /** + */ + function level_decode(level_string: string): enum_level; } declare namespace lib_plankton.log { /** - * output for writing log entries to stdout + * @todo use label */ - class class_channel_stdout extends class_channel { - /** - */ - add(entry: type_entry): void; - } -} -declare namespace lib_plankton.log { + function get_logger_logic(logger_data: type_logger_data): type_logger_logic; /** */ - class class_channel_file extends class_channel { - /** - * the path of the log file - */ - private path; - /** - */ - private human_readable; - /** - * [constructor] - */ - constructor(path: string, human_readable: boolean); - /** - */ - add(entry: type_entry): void; - } -} -declare namespace lib_plankton.log { + function format_entry(format_definition: type_format_definition, entry: type_entry): string; /** */ - class class_channel_email extends class_channel { + function parse_format_definition(format_definition_raw: any): type_format_definition; +} +declare namespace lib_plankton.log.channel.filtered { + /** + */ + type type_predicate = ((entry: type_entry) => boolean); + /** + */ + type type_subject = { /** + * @todo check if it has to be logic */ - private smtp_credentials; + core: type_channel_logic; + predicate: type_predicate; + }; + /** + */ + function predicate_incident(substring: string): type_predicate; + /** + */ + function predicate_level(threshold: enum_level): type_predicate; + /** + */ + function predicate_tag(tag: string): type_predicate; + /** + * combines other predicates in disjunctive normal form + */ + function predicate_complex(definition: Array>): type_predicate; + /** + */ + function send(subject: type_subject, entry: type_entry): void; + /** + */ + function logic(subject: type_subject): type_channel_logic; +} +declare namespace lib_plankton.log.channel.minlevel { + /** + */ + type type_subject = { /** + * @todo check if it has to be logic */ - private sender; - /** - */ - private receivers; - /** - * [constructor] - */ - constructor(smtp_credentials: { + core: type_channel_logic; + threshold: enum_level; + }; + /** + */ + function send(subject: type_subject, entry: type_entry): void; + /** + */ + function logic(subject: type_subject): type_channel_logic; +} +declare namespace lib_plankton.log.channel.std { + /** + */ + type type_subject = { + target: ("stdout" | "stderr"); + format: type_format_definition; + }; + /** + */ + function send(subject: type_subject, entry: type_entry): void; + /** + */ + function logic(subject: type_subject): type_channel_logic; +} +declare namespace lib_plankton.log.channel.file { + /** + */ + type type_subject = { + path: string; + format: type_format_definition; + }; + /** + */ + function send(subject: type_subject, entry: type_entry): void; + /** + */ + function logic(subject: type_subject): type_channel_logic; +} +declare namespace lib_plankton.log.channel.notify { + /** + */ + type type_subject = {}; + /** + * @todo tags + */ + function send(subject: type_subject, entry: type_entry): void; + /** + */ + function logic(subject: type_subject): type_channel_logic; +} +declare namespace lib_plankton.log.channel.email { + /** + */ + type type_subject = { + smtp_credentials: { host: string; port: int; username: string; password: string; - }, sender: string, receivers: Array); - /** - */ - add(entry: type_entry): void; - } -} -declare namespace lib_plankton.log { - /** - * output for desktop notifications via "libnotify" - */ - class class_channel_notify extends class_channel { - /** - */ - add(entry: type_entry): void; - } -} -declare namespace lib_plankton.log { - /** - * decorator for filtering out log entries below a certain level threshold - */ - class class_channel_minlevel extends class_channel { - /** - */ - private core; - /** - */ - private threshold; - /** - */ - constructor(core: class_channel, threshold: enum_level); - /** - */ - add(entry: type_entry): void; - } -} -declare namespace lib_plankton.log { - /** - */ - function channel_make(description: { - kind: string; - data?: { - [key: string]: any; }; - }): class_channel; + sender: string; + receivers: Array; + }; + /** + * @todo tags + */ + function send(subject: type_subject, entry: type_entry): void; /** */ - type type_configuration = Array; - /** - */ - function conf_default(): type_configuration; + function logic(subject: type_subject): type_channel_logic; } declare namespace lib_plankton.log { /** - * pushes a new configuration on the stack and activates it */ - function conf_push(channels: type_configuration): void; - /** - * pops the current active configuration from the stack - */ - function conf_pop(): void; - /** - * consumes a log entry, i.e. sends it to the currently active outputs - */ - function add(entry: type_entry): void; + function get_channel_logic(channel_description: type_channel_description): type_channel_logic; +} +declare namespace lib_plankton.log { /** */ - function debug(incident: string, details?: Record): void; + function default_logger(): type_logger_data; +} +declare namespace lib_plankton.log { /** */ - function info(incident: string, details?: Record): void; + function set_main_logger(logger_data: type_logger_data): void; /** + * consumes a log entry, i.e. sends it to all channels */ - function notice(incident: string, details?: Record): void; + function send_(logger: type_logger_logic, entry: type_entry): void; /** + * [convenience] + * + * @todo rename to "send" */ - function warning(incident: string, details?: Record): void; + function debug_(logger: type_logger_logic, incident: string, { "tags": option_tags, "details": option_details, }?: { + tags?: Array; + details?: any; + }): void; /** + * [convenience] + * + * @todo rename to "info" */ - function error(incident: string, details?: Record): void; + function info_(logger: type_logger_logic, incident: string, { "tags": option_tags, "details": option_details, }?: { + tags?: Array; + details?: any; + }): void; + /** + * [convenience] + * + * @todo rename to "notice" + */ + function notice_(logger: type_logger_logic, incident: string, { "tags": option_tags, "details": option_details, }?: { + tags?: Array; + details?: any; + }): void; + /** + * [convenience] + * + * @todo rename to "warning" + */ + function warning_(logger: type_logger_logic, incident: string, { "tags": option_tags, "details": option_details, }?: { + tags?: Array; + details?: any; + }): void; + /** + * [convenience] + * + * @todo rename to "error" + */ + function error_(logger: type_logger_logic, incident: string, { "tags": option_tags, "details": option_details, }?: { + tags?: Array; + details?: any; + }): void; + /** + * [convenience] + */ + function _send(entry: type_entry): void; + /** + * [convenience] + */ + function _debug(incident: string, { "tags": option_tags, "details": option_details, }?: { + tags?: Array; + details?: any; + }): void; + /** + * [convenience] + */ + function _info(incident: string, { "tags": option_tags, "details": option_details, }?: { + tags?: Array; + details?: any; + }): void; + /** + * [convenience] + */ + function _notice(incident: string, { "tags": option_tags, "details": option_details, }?: { + tags?: Array; + details?: any; + }): void; + /** + * [convenience] + */ + function _warning(incident: string, { "tags": option_tags, "details": option_details, }?: { + tags?: Array; + details?: any; + }): void; + /** + * [convenience] + */ + function _error(incident: string, { "tags": option_tags, "details": option_details, }?: { + tags?: Array; + details?: any; + }): void; + /** + * [convenience] + * + * @deprecated use ._debug instead! + */ + function debug(incident: string, details?: any, tags?: Array): void; + /** + * [convenience] + * + * @deprecated use ._info instead! + */ + function info(incident: string, details?: any, tags?: Array): void; + /** + * [convenience] + * + * @deprecated use ._notice instead! + */ + function notice(incident: string, details?: any, tags?: Array): void; + /** + * [convenience] + * + * @deprecated use ._warning instead! + */ + function warning(incident: string, details?: any, tags?: Array): void; + /** + * [convenience] + * + * @deprecated use ._error instead! + */ + function error(incident: string, details?: any, tags?: Array): void; } declare var plain_text_to_html: (text: string) => string; /** @@ -1155,7 +1196,7 @@ declare namespace lib_plankton.string { * @return {Array} * @author fenris */ - function split(chain: string, separator?: string): Array; + function split(chain: string, separator: string): Array; /** * @author neu3no */ @@ -1241,6 +1282,9 @@ declare namespace lib_plankton.string { /** */ function slice(str: string, size: int): Array; + /** + */ + function capitalize(str: string): string; } /** * @deprecated @@ -1718,21 +1762,56 @@ declare namespace lib_plankton.code { decode(x: type_flatten_to): type_flatten_from; } } +declare namespace lib_plankton.www_form { + /** + */ + type type_source = Record; + /** + */ + type type_target = string; + /** + * @author fenris + */ + function encode(source: type_source): type_target; + /** + * @author fenris + */ + function decode(target: type_target): type_source; +} +declare namespace lib_plankton.www_form { + /** + * @author fenris + */ + class class_www_form implements lib_plankton.code.interface_code { + /** + * @author fenris + */ + constructor(); + /** + * @implementation + * @author fenris + */ + encode(source: type_source): type_target; + /** + * @implementation + * @author fenris + */ + decode(target: type_target): type_source; + } +} declare namespace lib_plankton.url { /** * @author fenris */ type type_url = { - protocol?: string; - host?: string; - username?: string; - password?: string; - port?: int; - path?: Array; - arguments?: { - [key: string]: string; - }; - hash?: string; + scheme: (null | string); + host: (null | string); + username: (null | string); + password: (null | string); + port: (null | int); + path: (null | string); + query: (null | string); + hash: (null | string); }; } declare namespace lib_plankton.url { @@ -2730,13 +2809,26 @@ declare namespace lib_plankton.session { } declare namespace lib_plankton.json { /** - * @author fenris */ - function encode(x: any, formatted?: boolean): string; + type type_source = any; + /** + */ + type type_target = string; /** * @author fenris */ - function decode(x: string): any; + export function encode(source: type_source, options?: { + formatted?: boolean; + }): type_target; + /** + * @author fenris + */ + export function decode(target: type_target): type_source; + /** + * @author fenris + */ + export function implementation_code(): lib_plankton.code.type_code; + export {}; } declare namespace lib_plankton.json { /** @@ -2762,114 +2854,105 @@ declare namespace lib_plankton.json { declare namespace lib_plankton.object { /** * @author fenris + * @deprecated use the "??" operator instead */ - function fetch(object: Object, fieldname: string, fallback?: type_value, escalation?: int): type_value; + function fetch(object: Object, fieldname: string, options?: { + fallback?: type_value; + escalate?: boolean; + }): type_value; /** - * @author fenris */ - function map(object_from: { - [key: string]: type_from; - }, transformator: (value_from: type_from, key?: string) => type_to): { - [key: string]: type_to; - }; + function map(object_from: Record, transformator: ((value_from: type_from, key?: string) => type_to)): Record; /** - * @desc gibt ein Objekt mit bestimmten Einträgen des Eingabe-Objekts zurück - * @author fenris + * gibt ein Objekt mit bestimmten Einträgen des Eingabe-Objekts zurück */ - function filter(object_from: { - [key: string]: type_value; - }, predicate: (value_from: type_value, key?: string) => boolean): { - [key: string]: type_value; - }; + function filter(object_from: Record, predicate: ((value_from: type_value, key?: string) => boolean)): Record; /** - * @desc wandelt ein Array mit Einträgen der Form {key,value} in ein entsprechendes Objekt um - * @author fenris + * wandelt ein Array mit Einträgen der Form {key,value} in ein entsprechendes Objekt um + * + * @deprecated use Object.fromEntries instead! */ function from_array(array: Array<{ key: string; value: type_value; - }>): { - [key: string]: type_value; - }; + }>): Record; /** - * @desc wandelt ein Objekt in ein entsprechendes Array mit Einträgen der Form {key,value} um - * @author fenris + * wandelt ein Objekt in ein entsprechendes Array mit Einträgen der Form {key,value} um + * + * @deprecated use Object.entries insetad! */ - function to_array(object: { - [key: string]: type_value; - }): Array<{ + function to_array(object: Record): Array<{ key: string; value: type_value; }>; /** - * @desc gibt eine Liste von Schlüsseln eines Objekts zurück - * @author fenris + * gibt eine Liste von Schlüsseln eines Objekts zurück + * + * @deprecated use Object.keys instead! */ - function keys(object: { - [key: string]: any; - }): Array; + function keys(object: Record): Array; /** - * @desc gibt eine Liste von Werten eines Objekts zurück - * @author fenris + * gibt eine Liste von Werten eines Objekts zurück + * + * @deprecated use Object.values instead! */ - function values(object: { - [key: string]: type_value; - }): Array; + function values(object: Record): Array; /** - * @desc liest ein Baum-artiges Objekt an einer bestimmten Stelle aus - * @author fenris + * liest ein Baum-artiges Objekt an einer bestimmten Stelle aus */ - function path_read(object: Object, path: string, fallback?: type_value, escalation?: int): type_value; + function path_read(object: Object, path: string, options?: { + fallback?: type_value; + escalate?: boolean; + }): type_value; /** - * @desc schreibt einen Wert an eine bestimmte Stelle in einem Baum-artigen Objekt - * @author fenris + * schreibt einen Wert an eine bestimmte Stelle in einem Baum-artigen Objekt */ function path_write(object: Object, path: string, value: type_value, construct?: boolean): void; /** - * @desc prüft ob ein Objekt einem bestimmten Muster entspricht - * @param {Object} object das zu prüfende Objekt - * @param {Object} pattern das einzuhaltende Muster - * @param {Function} connlate eine Funktion zum Feststellen der Gleichheit von Einzelwerten - * @author fenris + * prüft ob ein Objekt einem bestimmten Muster entspricht + * + * @deprecated not very useful */ - function matches(object: Object, pattern: Object, collate?: typeof instance_collate): boolean; + function matches(object: Record, pattern: Record, options?: { + collate?: ((value_pattern: type_value_pattern, value_object: type_value_object) => boolean); + }): boolean; /** - * @desc erzeugt eine Projektion eines Baum-artigen Objekts in ein Listen-artiges Objekt - * @param {string} [separator] welches Zeichen als Trenner zwischen zwei Pfad-Schritten verwendet werden soll - * @author fenris + * erzeugt eine Projektion eines Baum-artigen Objekts in ein Listen-artiges Objekt */ - function flatten(value: any, separator?: string, key_for_element?: (index: int) => string): Object; + function flatten(value: any, options?: { + separator?: string; + key_for_array_element?: ((index: int) => string); + }): Record; /** - * @author fenris + * @deprecated use Object.assign instead! */ - function clash(x: { - [key: string]: any; - }, y: { - [key: string]: any; - }, { "overwrite": overwrite, "hooks": { "existing": hook_existing, }, }?: { + function clash(x: Record, y: Record, options?: { overwrite?: boolean; hooks?: { - existing?: (key?: string, value_old?: any, value_new?: any) => void; + existing?: ((key?: string, value_old?: any, value_new?: any) => void); }; - }): { - [key: string]: any; - }; + }): Record; + /** + * @deprecated use Object.assign instead! + */ + function patch(core: (null | Record), mantle: (null | Record), options?: { + deep?: boolean; + path?: (null | string); + }): void; + /** + * @deprecated use Object.assign instead! + */ + function patched(core: Record, mantle: Record, options?: { + deep?: boolean; + }): Record; + /** + * @deprecated use Object.assign instead! + */ + function attached(object: Record, key: string, value: any): Record; /** * @author fenris */ - function patch(core: Object, mantle: Object, deep?: boolean, path?: string): void; - /** - * @author fenris - */ - function patched(core: Object, mantle: Object, deep?: boolean): Object; - /** - * @author fenris - */ - function attached(object: Object, key: string, value: any): Object; - /** - * @author fenris - */ - function copy(object: Object): Object; + function copy(object: Record): Record; } declare namespace lib_plankton.markdown { /** @@ -3015,6 +3098,52 @@ declare namespace lib_plankton.api { generate_documentation(): string; } } +declare namespace lib_plankton.http_base { + /** + */ + type type_request = { + scheme: ("http" | "https"); + host: (null | string); + path: string; + version: string; + method: type_method; + query: (null | string); + headers: Record; + body: (null | Buffer); + }; + /** + */ + type type_response = { + version: (null | string); + status_code: type_status_code; + headers: Record; + body: (null | Buffer); + }; +} +declare namespace lib_plankton.http_base { + /** + */ + function encode_request(encode_method: ((method: type_method) => string), request: type_request): string; + /** + */ + function decode_request(decode_method: ((method_raw: string) => type_method), has_body: ((method: type_method) => boolean), request_raw: string): type_request; + /** + */ + function encode_response(encode_status_code: ((status_code: type_status_code) => string), get_status_text: ((status_code: type_status_code) => string), response: type_response): string; + /** + */ + function decode_response(decode_status_code: ((status_code_raw: string) => type_status_code), response_raw: string): type_response; + /** + * executes an HTTP request + * + * @todo define type_signal + */ + function call(has_body: ((method: type_method) => boolean), encode_method: ((method: type_method) => string), decode_status_code: ((status_code_raw: string) => type_status_code), request: type_request, { "timeout": option_timeout, "follow_redirects": option_follow_redirects, "implementation": option_implementation, }?: { + timeout?: (null | float); + follow_redirects?: boolean; + implementation?: ("fetch" | "http_module"); + }): Promise>; +} declare namespace lib_plankton.http { /** * @author fenris @@ -3029,33 +3158,85 @@ declare namespace lib_plankton.http { patch = "patch" } /** - * @author fenris */ - type type_request = { - scheme: ("http" | "https"); - host: (null | string); - path: string; - version: string; - method: enum_method; - query: (null | string); - headers: Record; - body: (null | Buffer); - }; + enum enum_status_code { + continue_ = 100, + switching_protocols = 101, + early_hints = 103, + ok = 200, + created = 201, + accepted = 202, + non_authoritative_information = 203, + no_content = 204, + reset_content = 205, + partial_coentent = 206, + multiple_choices = 300, + moved_permanently = 301, + found = 302, + see_other = 303, + not_modified = 304, + temporary_redirect = 307, + permanent_redirect = 308, + bad_request = 400, + unauthorized = 401, + payment_required = 402, + forbidden = 403, + not_found = 404, + method_not_allowed = 405, + not_acceptable = 406, + proxy_authentication_required = 407, + request_timeout = 408, + conflict = 409, + gone = 410, + length_required = 411, + precondition_failed = 412, + payload_too_large = 413, + uri_too_long = 414, + unsupported_media_type = 415, + range_not_satisfiable = 416, + expectation_failed = 417, + i_m_a_teapot = 418, + unprocessable_entity = 422, + too_early = 425, + upgrade_required = 426, + precondition_required = 428, + too_many_requests = 429, + request_header_fields_too_large = 431, + unavailable_for_legal_reasons = 451, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503, + gateway_timeout = 504, + http_version_not_supported = 505, + variant_also_negotiates = 506, + insufficient_storage = 507, + loop_detected = 508, + not_extended = 510, + network_authentication = 511 + } /** * @author fenris */ - type type_response = { - version: (null | string); - status_code: int; - headers: Record; - body: Buffer; - }; + type type_request = lib_plankton.http_base.type_request; + /** + * @author fenris + */ + type type_response = lib_plankton.http_base.type_response; } declare namespace lib_plankton.http { /** * @author fenris */ function encode_method(method: enum_method): string; + /** + * @author fenris + */ + function decode_method(method_raw: string): enum_method; + /** + * @author fenris + */ + function has_body(method: enum_method): boolean; /** * @author fenris */ @@ -3077,53 +3258,13 @@ declare namespace lib_plankton.http { * * @todo define type_signal */ - function call(request: type_request, options?: { + function call(request: type_request, { "timeout": option_timeout, "follow_redirects": option_follow_redirects, "implementation": option_implementation, }?: { timeout?: (null | float); follow_redirects?: boolean; implementation?: ("fetch" | "http_module"); }): Promise; } -declare namespace lib_plankton.http { - /** - * @author fenris - */ - class class_http_request implements lib_plankton.code.interface_code { - /** - * @author fenris - */ - constructor(); - /** - * @implementation - * @author fenris - */ - encode(x: type_request): string; - /** - * @implementation - * @author fenris - */ - decode(x: string): type_request; - } - /** - * @author fenris - */ - class class_http_response implements lib_plankton.code.interface_code { - /** - * @author fenris - */ - constructor(); - /** - * @implementation - * @author fenris - */ - encode(x: type_response): string; - /** - * @implementation - * @author fenris - */ - decode(x: string): type_response; - } -} -declare namespace lib_plankton.rest { +declare namespace lib_plankton.rest_base { /** */ type type_oas_schema = ({} | { @@ -3158,7 +3299,6 @@ declare namespace lib_plankton.rest { /** */ type type_execution = ((stuff: { - version: (null | string); headers: Record; path_parameters: Record; query_parameters: Record; @@ -3166,11 +3306,11 @@ declare namespace lib_plankton.rest { }) => Promise<{ status_code: int; data: type_output; + headers?: (null | Record); }>); /** */ type type_restriction = ((stuff: { - version: (null | string); headers: Record; path_parameters: Record; query_parameters: Record; @@ -3179,22 +3319,22 @@ declare namespace lib_plankton.rest { */ type type_operation = { action_name: string; - query_parameters: Array<{ + query_parameters: ((version: (null | string)) => Array<{ name: string; description: (null | string); required: boolean; - }>; - request_body_mimetype: string; - request_body_decode: ((http_request_body: Buffer, http_request_header_content_type: (null | string)) => any); - response_body_mimetype: string; - response_body_encode: ((output: any) => Buffer); + }>); input_schema: ((version: (null | string)) => type_oas_schema); output_schema: ((version: (null | string)) => type_oas_schema); + request_body_mimetype: ((version: (null | string)) => string); + request_body_decode: ((version: (null | string)) => (http_request_body: Buffer, http_request_header_content_type: (null | string)) => any); + response_body_mimetype: ((version: (null | string)) => string); + response_body_encode: ((version: (null | string)) => ((output: any) => (null | Buffer))); }; /** */ type type_routenode = { - operations: Record>; + operations: Record>; sub_branch: Record; sub_wildcard: (null | { name: string; @@ -3217,16 +3357,110 @@ declare namespace lib_plankton.rest { set_access_control_headers: boolean; authentication: ({ kind: "none"; - parameters: {}; + data: {}; } | { kind: "key_header"; - parameters: { + data: { name: string; }; }); }; + /** + */ + type type_action_options = { + active?: ((version: (null | string)) => boolean); + restriction?: ((version: (null | string)) => type_restriction); + execution?: ((version: (null | string)) => type_execution); + title?: ((version: (null | string)) => (null | string)); + description?: ((version: (null | string)) => (null | string)); + query_parameters?: ((version: (null | string)) => Array<{ + name: string; + description: (null | string); + required: boolean; + }>); + input_schema?: ((version: (null | string)) => type_oas_schema); + output_schema?: ((version: (null | string)) => type_oas_schema); + request_body_mimetype?: ((version: (null | string)) => string); + request_body_decode?: ((version: (null | string)) => (http_request_body: Buffer, http_request_header_content_type: (null | string)) => Promise); + response_body_mimetype?: ((version: (null | string)) => string); + response_body_encode?: ((version: (null | string)) => (output: any) => Promise); + }; } -declare namespace lib_plankton.rest { +declare namespace lib_plankton.rest_base { + /** + */ + function make(encode_http_method: ((http_method: type_http_method) => string), { "title": option_title, "versioning_method": option_versioning_method, "versioning_header_name": option_versioning_header_name, "versioning_query_key": option_versioning_query_key, "header_parameters": option_header_parameters, "set_access_control_headers": option_set_access_control_headers, "authentication": option_authentication, "actions": option_actions, }?: { + title?: (null | string); + versioning_method?: ("none" | "path" | "header" | "query"); + versioning_header_name?: (null | string); + versioning_query_key?: (null | string); + header_parameters?: Array<{ + name: string; + description: (null | string); + required: boolean; + }>; + set_access_control_headers?: boolean; + authentication?: ({ + kind: "none"; + data: {}; + } | { + kind: "key_header"; + data: { + name: string; + }; + }); + actions?: Array<{ + http_method: type_http_method; + path: string; + options: type_action_options; + }>; + }): type_rest; + /** + */ + function register(encode_http_method: ((http_method: type_http_method) => string), rest: type_rest, http_method: type_http_method, path: string, { "active": option_active, "execution": option_execution, "restriction": option_restriction, "title": option_title, "description": option_description, "query_parameters": option_query_parameters, "input_schema": option_input_schema, "output_schema": option_output_schema, "request_body_mimetype": option_request_body_mimetype, "request_body_decode": option_request_body_decode, "response_body_mimetype": option_response_body_mimetype, + /** + * @todo no "from"? + */ + "response_body_encode": option_response_body_encode, }?: type_action_options): void; + /** + * @todo check request body mimetype? + * @todo check query paramater validity + */ + function call(encode_http_method: ((http_method: type_http_method) => string), is_options_request: ((http_method: type_http_method) => boolean), decode_status_code: ((status_code_raw: int) => type_http_status_code), rest: type_rest, http_request: lib_plankton.http_base.type_request, { "checklevel_restriction": option_checklevel_restriction, "checklevel_input": option_checklevel_input, "checklevel_output": option_checklevel_output, "set_content_length": option_set_content_length, }?: { + checklevel_restriction?: lib_plankton.api.enum_checklevel; + checklevel_input?: lib_plankton.api.enum_checklevel; + checklevel_output?: lib_plankton.api.enum_checklevel; + set_content_length?: boolean; + }): Promise>; + /** + * @see https://swagger.io/specification/#openrest-object + */ + function to_oas(http_request_method_to_oas: ((http_request_method: type_method) => string), has_body: ((method: type_method) => boolean), rest: type_rest, { "version": option_version, "servers": option_servers, }?: { + version?: (null | string); + servers?: Array; + }): any; +} +declare namespace lib_plankton.rest_http { + /** + */ + type type_oas_schema = lib_plankton.rest_base.type_oas_schema; + /** + */ + type type_execution = lib_plankton.rest_base.type_execution; + /** + */ + type type_restriction = lib_plankton.rest_base.type_restriction; + /** + */ + type type_operation = lib_plankton.rest_base.type_operation; + /** + */ + type type_routenode = lib_plankton.rest_base.type_routenode; + /** + */ + type type_rest = lib_plankton.rest_base.type_rest; +} +declare namespace lib_plankton.rest_http { /** */ function make(options?: { @@ -3242,64 +3476,32 @@ declare namespace lib_plankton.rest { set_access_control_headers?: boolean; authentication?: ({ kind: "none"; - parameters: {}; + data: {}; } | { kind: "key_header"; - parameters: { + data: { name: string; }; }); actions?: Array<{ http_method: lib_plankton.http.enum_method; path: string; - options: { - active?: ((version: string) => boolean); - restriction?: (null | type_restriction); - execution?: type_execution; - title?: (null | string); - description?: (null | string); - query_parameters?: Array<{ - name: string; - description: (null | string); - required: boolean; - }>; - input_schema?: ((version: string) => type_oas_schema); - output_schema?: ((version: string) => type_oas_schema); - request_body_mimetype?: string; - request_body_decode?: ((http_request_body: Buffer, http_request_header_content_type: (null | string)) => any); - response_body_mimetype?: string; - response_body_encode?: ((output: any) => Buffer); - }; + options: lib_plankton.rest_base.type_action_options; }>; }): type_rest; /** */ - function register(rest: type_rest, http_method: lib_plankton.http.enum_method, path: string, options: { - active?: ((version: string) => boolean); - restriction?: (null | type_restriction); - execution?: type_execution; - title?: (null | string); - description?: (null | string); - query_parameters?: Array<{ - name: string; - description: (null | string); - required: boolean; - }>; - input_schema?: ((version: (null | string)) => type_oas_schema); - output_schema?: ((version: (null | string)) => type_oas_schema); - request_body_mimetype?: string; - request_body_decode?: ((http_request_body: Buffer, http_request_header_content_type: (null | string)) => any); - response_body_mimetype?: string; - response_body_encode?: ((output: any) => Buffer); - }): void; + function register(rest: type_rest, http_method: lib_plankton.http.enum_method, path: string, options: lib_plankton.rest_base.type_action_options): void; /** * @todo check request body mimetype? * @todo check query paramater validity + * @todo improve status code mapping */ - function call(rest: type_rest, http_request: lib_plankton.http.type_request, options?: { + function call(rest: type_rest, http_request: lib_plankton.http.type_request, { "checklevel_restriction": option_checklevel_restriction, "checklevel_input": option_checklevel_input, "checklevel_output": option_checklevel_output, "set_content_length": option_set_content_length, }?: { checklevel_restriction?: lib_plankton.api.enum_checklevel; checklevel_input?: lib_plankton.api.enum_checklevel; checklevel_output?: lib_plankton.api.enum_checklevel; + set_content_length?: boolean; }): Promise; /** * @see https://swagger.io/specification/#openrest-object @@ -3311,13 +3513,11 @@ declare namespace lib_plankton.rest { } declare namespace lib_plankton.server { /** - * @author fenris */ type type_metadata = { ip_address: string; }; /** - * @author fenris */ type type_subject = { host: string; @@ -3327,7 +3527,6 @@ declare namespace lib_plankton.server { serverobj: any; }; /** - * @author fenris */ function make(handle: ((input: string, metadata?: type_metadata) => Promise), options?: { host?: string; @@ -3335,17 +3534,14 @@ declare namespace lib_plankton.server { threshold?: (null | float); }): type_subject; /** - * @author fenris * @deprecated */ function make_old(port: int, handle: ((input: string, metadata?: type_metadata) => Promise)): type_subject; /** - * @author fenris * @see https://nodejs.org/api/net.html#serverlistenport-host-backlog-callback */ function start(subject: type_subject): Promise; /** - * @author fenris */ function kill(subject: type_subject): void; } diff --git a/lib/plankton/plankton.js b/lib/plankton/plankton.js index 3ae4046..1cc8696 100644 --- a/lib/plankton/plankton.js +++ b/lib/plankton/plankton.js @@ -1,18 +1,3 @@ -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); /* This file is part of »bacterio-plankton:base«. @@ -115,7 +100,7 @@ along with »bacterio-plankton:base«. If not, see this.actions[id](information)); } - }; + } /** * @author frac */ - class_observer.prototype.rollout = function () { - var _this = this; - this.buffer.forEach(function (information) { return _this.notify(information, false); }); + rollout() { + this.buffer.forEach(information => this.notify(information, false)); this.buffer = []; - }; - return class_observer; -}()); + } +} /** * @author frac */ @@ -389,27 +369,23 @@ along with »bacterio-plankton:base«. If not, see x.toString()).join(",") + "]")); + } +} /* This file is part of »bacterio-plankton:base«. @@ -438,9 +414,8 @@ var lib_plankton; * * @author fenris */ - function get_current_timestamp(rounded) { - if (rounded === void 0) { rounded = false; } - var x = (Date.now() / 1000); + function get_current_timestamp(rounded = false) { + const x = (Date.now() / 1000); return (rounded ? Math.round(x) : x); ; } @@ -451,6 +426,24 @@ var lib_plankton; return Object.assign(core, mantle); } base.object_merge = object_merge; + /** + */ + function buffer_show(buffer, { "block_size": option_block_size = 20, "break_char": option_break_char = "\n", } = {}) { + let output = ""; + let count = 0; + // @ts-ignore + for (const entry of buffer) { + count = ((count + 1) % option_block_size); + output += ((typeof (entry) === "string") + ? + entry.charCodeAt(0) + : + entry).toString(16).toUpperCase().padStart(2, "0"); + output += ((count === 0) ? option_break_char : " "); + } + return output; + } + base.buffer_show = buffer_show; })(base = lib_plankton.base || (lib_plankton.base = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -584,6 +577,9 @@ var lib_plankton; */ class class_pod { constructor(subject) { this.subject = subject; } + tear() { return this.subject; } + static empty() { return (new class_pod(pod.make_empty())); } + static filled(value) { return (new class_pod(pod.make_filled(value))); } is_empty() { return (!pod.is_filled(this.subject)); } is_filled() { return pod.is_filled(this.subject); } cull() { return pod.cull(this.subject); } @@ -773,89 +769,6 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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:call«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var call; - (function (call) { - /** - */ - class CancellablePromise extends Promise { - /** - */ - constructor(executor) { - super((resolve, reject) => { }); - this.subject = (new Promise((resolve, reject) => { - Promise.race([ - new Promise(executor), - new Promise((resolve_, reject_) => { - this.interval = setInterval(() => { - if (!this.cancelled) { - // do nothing - } - else { - reject_(new Error("cancelled")); - this.clear(); - } - }, 0); - }), - ]) - .then(resolve, reject); - })); - this.cancelled = false; - this.interval = null; - } - /** - */ - clear() { - if (this.interval === null) { - // do nothing - } - else { - clearInterval(this.interval); - this.interval = null; - } - } - /** - */ - then(onfulfilled, onrejected) { - this.clear(); - return this.subject.then(onfulfilled, onrejected); - } - /** - */ - catch(x) { - this.clear(); - return this.subject.catch(x); - } - /** - */ - cancel() { - this.cancelled = true; - this.clear(); - } - } - call.CancellablePromise = CancellablePromise; - })(call = lib_plankton.call || (lib_plankton.call = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:call«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:call« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:call« 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 -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:call«. If not, see . */ @@ -1385,6 +1298,31 @@ var lib_plankton; return result; } call.convey = convey; + /** + */ + class class_value_wrapper { + /** + */ + constructor(value) { + this.value = value; + } + /** + */ + convey(function_) { + return (new class_value_wrapper(function_(this.value))); + } + /** + */ + cull() { + return this.value; + } + } + /** + */ + function wrap(value) { + return (new class_value_wrapper(value)); + } + call.wrap = wrap; /** * @author fenris */ @@ -1491,66 +1429,131 @@ var lib_plankton; } call.distinguish = distinguish; /** - * rate limiting algorithm, based on the idea of mana (magic power) in video games: - * - an actor has a fixed mana capacity, i.e. the maximum amount of available power - * - an actor has a fixed rate of mana regeneration, i.e. how fast the power is filled up (linear growth) - * - an action has a defined mana heft, i.e. how much power is required and deducted in order to execute it - * - mana states are represented by snapshots, i.e. the amount of power at a certain point in time - * - * @author fenris */ - async function rate_limit_check(setup, heft) { - if (heft > setup.capacity) { - return Promise.resolve({ - "granted": false, - "seconds": null, - }); + function try_catch_wrap(get_value) { + try { + return { + "value": get_value(), + "error": null, + }; } - else { - // get current value - const current_timestamp = (Date.now() / 1000); - const old_snapshot_raw = (await setup.get_snapshot()); - const old_snapshot = (old_snapshot_raw - ?? - { "timestamp": current_timestamp, "value": setup.capacity }); - const seconds_passed = (current_timestamp - old_snapshot.timestamp); - const current_value = Math.min(setup.capacity, (old_snapshot.value - + - (setup.regeneration_rate - * - seconds_passed))); - // analyze - if (current_value < heft) { - // too less - const seconds_needed = ((setup.regeneration_rate <= 0) - ? null - : ((heft - old_snapshot.value) / setup.regeneration_rate)); - return Promise.resolve({ - "granted": false, - "seconds": ((seconds_needed === null) ? null : (seconds_needed - seconds_passed)), - }); - } - else { - // enough -> update snapshot - const new_value = (current_value - heft); - // set_snapshot - if (old_snapshot_raw === null) { - await setup.set_snapshot({ "timestamp": current_timestamp, "value": new_value }); - } - else { - await setup.update_snapshot(current_timestamp, (new_value - old_snapshot.value)); - } - return Promise.resolve({ - "granted": true, - "seconds": 0, - }); - } + catch (error) { + return { + "value": null, + "error": error, + }; } } - call.rate_limit_check = rate_limit_check; + call.try_catch_wrap = try_catch_wrap; + /** + */ + function try_catch_wrap_async(get_value) { + return (get_value() + .then((value) => Promise.resolve({ + "value": value, + "error": null, + })) + .catch((reason) => Promise.resolve({ + "value": null, + "error": reason, + }))); + } + call.try_catch_wrap_async = try_catch_wrap_async; })(call = lib_plankton.call || (lib_plankton.call = {})); })(lib_plankton || (lib_plankton = {})); /* +This file is part of »bacterio-plankton:pair«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:pair« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:pair« 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 +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:pair«. If not, see . + */ +/* +This file is part of »bacterio-plankton:pair«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:pair« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:pair« 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 +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:pair«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var pair; + (function (pair_1) { + /** + */ + function swap(pair) { + return { + "first": pair.second, + "second": pair.first + }; + } + pair_1.swap = swap; + /** + */ + function show(pair, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "show_first": instance_show, + "show_second": instance_show + }, options); + return ("(" + + + options.show_first(pair.first) + + + "," + + + options.show_second(pair.second) + + + ")"); + } + pair_1.show = show; + })(pair = lib_plankton.pair || (lib_plankton.pair = {})); +})(lib_plankton || (lib_plankton = {})); +"use strict"; +/* +This file is part of »bacterio-plankton:list«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:list« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:list« 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 +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:list«. If not, see . + */ +/* This file is part of »bacterio-plankton:list«. Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' @@ -1574,46 +1577,32 @@ var lib_plankton; var list; (function (list_1) { /** - * @desc returns a certain list of integer numbers - * @param {int} from - * @param {int} to - * @param {int} step; default: 1 - * @author fenris + * returns a certain list of integer numbers */ - function range(from, to, step) { - if (step === void 0) { step = 1; } + function range(from, to, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "step": 1 + }, options); var list = []; - for (var value = from; value <= to; value += step) { + for (var value = from; value <= to; value += options.step) { list.push(value); } return list; } list_1.range = range; /** - * @desc returns a certain list of integer numbers - * @param {int} length - * @author fenris + * returns a certain list of consecutiv integer numbers, beginning with 0 */ function sequence(length) { return range(0, length - 1); } list_1.sequence = sequence; /** - * @author fenris */ - function from_structure(structure) { - var list = []; - for (var index = 0; index < structure.length; index += 1) { - list.push(structure[index]); - } - return list; - } - list_1.from_structure = from_structure; - /** - * @author fenris - */ - function from_iterator(iterator /* : Iterator*/) { + function from_iterator(iterator) { var list = []; + // @ts-ignore for (var _i = 0, iterator_1 = iterator; _i < iterator_1.length; _i++) { var element = iterator_1[_i]; list.push(element); @@ -1622,45 +1611,52 @@ var lib_plankton; } list_1.from_iterator = from_iterator; /** - * @author fenris */ - function empty(list) { + function is_empty(list) { return (list.length <= 0); } - list_1.empty = empty; + list_1.is_empty = is_empty; /** - * @desc creates a list, which contains pairs of elements from the input lists (corresponding by index) - * @param {boolean} [cut] whether the result list will be as long as the shortest input list or an exception is thrown if they have different lengths; default: true - * @author fenris + * combines two lists into one + * + * @param {boolean} [options.cut] whether the result list will be as long as the shortest input list or an exception is thrown if they have different lengths; default: true */ - function zip(list1, list2, cut) { - if (cut === void 0) { cut = true; } - var empty1 = empty(list1); - var empty2 = empty(list2); - if (empty1 || empty2) { - if (cut || (empty1 && empty2)) { + function zip(list_first, list_second, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "cut": true + }, options); + var empty_first = is_empty(list_first); + var empty_second = is_empty(list_second); + if (empty_first || empty_second) { + if (options.cut || (empty_first && empty_second)) { return []; } else { - var message = "lists have different lengths"; - throw (new Error(message)); + throw (new Error("lists have different lengths")); } } else { - return ([{ "first": list1[0], "second": list2[0] }] - .concat(zip(list1.slice(1), list2.slice(1), cut))); + return ([{ "first": list_first[0], "second": list_second[0] }] + .concat(zip(list_first.slice(1), list_second.slice(1), { + "cut": options.cut + }))); } } list_1.zip = zip; /** - * @desc checks whether two lists are equal + * checks whether two lists are equal + * * @todo define common function "equals" and default predicate to - * @author fenris */ - function equals(list1, list2, collate) { - if (collate === void 0) { collate = instance_collate; } + function equals(list1, list2, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "collate_element": instance_collate + }, options); if (list1.length == list2.length) { - return zip(list1, list2, true).every(function (pair) { return collate(pair.first, pair.second); }); + return (zip(list1, list2, { "cut": true }) + .every(function (pair) { return options.collate_element(pair.first, pair.second); })); } else { return false; @@ -1668,63 +1664,62 @@ var lib_plankton; } list_1.equals = equals; /** - * @author fenris - */ - function reversed(list) { - var list_ = clone(list); - list_.reverse(); - return list_; - } - list_1.reversed = reversed; - /** - * @author fenris - */ - function sorted(list, order) { - var list_ = clone(list); - list_.sort(order); - return list_; - } - list_1.sorted = sorted; - /** - * @desc creates a list with the elements from the input list, which fulfil a certain predicate - * @author fenris + * creates a list with the elements from the input list, which fulfil a certain predicate (~ filter) */ function keep(list, predicate) { - return list.filter(predicate); + return (list + .filter(function (element, index) { return predicate(element); })); } list_1.keep = keep; /** - * @desc creates a list with the elements from the input list, which do not fulfil a certain predicate - * @author fenris + * creates a list with the elements from the input list, which do not fulfil a certain predicate (~ dual filter) */ function drop(list, predicate) { - return list.filter(function (element, index) { return (!predicate(element, index)); }); + return (list + .filter(function (element, index) { return (!predicate(element)); })); } list_1.drop = drop; /** - * @desc returns a list with no duplicates - * @author fenris */ - function clean(list, collate) { - if (collate === void 0) { collate = instance_collate; } + function filter_inplace(list, predicate) { + var index = 0; + while (index < list.length) { + var element = list[index]; + if (predicate(element)) { + index += 1; + } + else { + list.splice(index, 1); + } + } + } + list_1.filter_inplace = filter_inplace; + /** + * returns a list with no duplicates (like unix' "unique") + */ + function cleaned(list, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "collate_element": instance_collate + }, options); var list_ = []; list.forEach(function (element) { - if (!list_.some(function (element_) { return collate(element, element_); })) { + if (!list_.some(function (element_) { return options.collate_element(element, element_); })) { list_.push(element); } + else { + // do nothing + } }); return list_; } - list_1.clean = clean; + list_1.cleaned = cleaned; /** - * @author fenris + * creates a binary partition of the list according to a given predicate */ - function clone(list) { - return keep(list, function (x) { return true; }); - } - list_1.clone = clone; function separate(list, predicate) { - return (list.reduce(function (seperation, element) { + return (list + .reduce(function (seperation, element) { return (predicate(element) ? { "yes": seperation.yes.concat([element]), "no": seperation["no"] } : { "yes": seperation.yes, "no": seperation["no"].concat([element]) }); @@ -1733,11 +1728,35 @@ var lib_plankton; list_1.separate = separate; ; /** - * die Liste in gleich große Blöcke zerlegen - * - * @author fenris */ - function split(list, chunk_size) { + function clone(list) { + return keep(list, function (x) { return true; }); + } + list_1.clone = clone; + /** + */ + function reversed(list) { + var list_ = clone(list); + list_.reverse(); + return list_; + } + list_1.reversed = reversed; + /** + * @todo use Array.toSorted? + */ + function sorted(list, options) { + options = Object.assign({ + "compare_element": instance_compare + }, options); + var list_ = clone(list); + list_.sort(function (x, y) { return (options.compare_element(x, y) ? -1 : +1); }); + return list_; + } + list_1.sorted = sorted; + /** + * die Liste in gleich große Blöcke zerlegen + */ + function chop(list, chunk_size) { var chunks = []; var index = 0; while (index < list.length) { @@ -1747,16 +1766,16 @@ var lib_plankton; } return chunks; } - list_1.split = split; + list_1.chop = chop; /** - * @author fenris */ - function group(list, collate) { - if (collate === void 0) { collate = instance_collate; } + function group(list, collate_element) { var result = []; list.forEach(function (element) { - var target = find(result, function (group) { return collate(group[0], element); }); - if (target == null) { + var target = result.find( + // @ts-ignore + function (group) { return collate_element(group[0], element); }); + if (target === undefined) { target = []; result.push(target); } @@ -1766,86 +1785,38 @@ var lib_plankton; } list_1.group = group; /** - * @author fenris - */ - function groups_(list, collate) { - if (collate === void 0) { collate = instance_collate; } - var message = "this function is deprecated; please use 'lib_plankton.list.group' instead (beware: it takes other arguments)"; - console.warn(message); - return group(list, collate); - } - list_1.groups_ = groups_; - /** - * @author fenris - * @deprecated - */ - function groups(list, sorting, hashing) { - var message = "this function is deprecated; please use 'lib_plankton.list.group' instead (beware: it takes other arguments)"; - console.warn(message); - list.sort(sorting); - return (groups_(list, function (x, y) { return (hashing(x) == hashing(y)); })); - } - list_1.groups = groups; - /** - * @desc searches for the first element in a list, that fulfils a given condition - * @author fenris - */ - function find(list, predicate, null_if_not_found) { - if (null_if_not_found === void 0) { null_if_not_found = true; } - var result = null; - var found = list.some(function (element, index) { - if (predicate(element)) { - result = element; - return true; - } - else { - return false; - } - }); - if (found) { - return result; - } - else { - if (null_if_not_found) { - return undefined; - } - else { - throw (new Error("no element in the list fulfils the given predicate")); - } - } - } - list_1.find = find; - /** - * @author fenris */ function has(list, predicate) { - try { - find(list, predicate, false); - return true; - } - catch (exception) { - return false; - } + return (list.find(predicate) !== undefined); } list_1.has = has; /** - * @author fenris + * @deprecate use Array.includes or Array.some */ - function contains(list, element, collation) { - if (collation === void 0) { collation = instance_collate; } - return has(list, function (element_) { return collation(element_, element); }); + function contains(list, element, options) { + options = Object.assign({ + "collate": instance_collate + }, options); + return has(list, function (element_) { return options.collate_element(element_, element); }); } list_1.contains = contains; - function max(list, targetfunction, compare) { - if (compare === void 0) { compare = instance_compare; } - if (empty(list)) { - var message = "the max-arg of an empty list is not defined"; - throw (new Error(message)); + /** + * retrieves the element and its index of the list, which has the maximum value + */ + function max(list, target_function, options) { + options = Object.assign({ + "compare_value": instance_compare + }, options); + if (is_empty(list)) { + throw (new Error("the max-arg of an empty list is not defined")); } else { - return (list.reduce(function (result, element, index) { - var value = targetfunction(element); - if ((result == null) || (!compare(value, result.value))) { + return (list + .reduce(function (result, element, index) { + var value = target_function(element); + if ((result == null) + || + (!options.compare_value(value, result.value))) { return { "index": index, "element": element, "value": value }; } else { @@ -1856,43 +1827,23 @@ var lib_plankton; } list_1.max = max; /** - * @desc retrieves the element and its index of the list, which has the mininum value - * @author fenris + * retrieves the element and its index of the list, which has the mininum value */ - function min(list, targetfunction, compare) { - if (compare === void 0) { compare = instance_compare; } - return max(list, targetfunction, function (x, y) { return compare(y, x); }); + function min(list, target_function, options) { + options = Object.assign({ + "compare_value": instance_compare + }, options); + return max(list, target_function, { + "compare_value": function (x, y) { return options.compare_value(y, x); } + }); } list_1.min = min; /** - * @desc retrieves the element of the list, which has the maximum output for a given target-function - * @author fenris - */ - function maxarg(list, targetfunction, compare) { - if (compare === void 0) { compare = instance_compare; } - var message = "this function is deprecated; please use 'lib_plankton.list.max' instead (beware: it has a different output)"; - console.warn(message); - return max(list, targetfunction, compare).element; - } - list_1.maxarg = maxarg; - /** - * @desc retrieves the element of the list, which has the minimum output for a given target-function - * @author fenris - */ - function minarg(list, targetfunction, compare) { - if (compare === void 0) { compare = instance_compare; } - var message = "this function is deprecated; please use 'lib_plankton.list.min' instead (beware: it has a different output)"; - console.warn(message); - return min(list, targetfunction, compare).element; - } - list_1.minarg = minarg; - /** - * @desc implements the idea of arithmetic distribution like in "(a+b)·(c+d) = (a·c)+(a·d)+(b·c)+(b·d)" + * implements the idea of arithmetic distribution like in "(a+b)·(c+d) = (a·c)+(a·d)+(b·c)+(b·d)" * example: distribute([[1,2],[3],[4,5,6]]) = [[1,3,4],[1,3,5],[1,3,6],[2,3,4],[2,3,5],[2,3,6]] - * @author fenris */ function distribute(lists) { - if (empty(lists)) { + if (is_empty(lists)) { return [[]]; } else { @@ -1904,138 +1855,49 @@ var lib_plankton; } list_1.distribute = distribute; /** - * @desc lexicographic order - * @author fenris - * @deprecated */ - function lex(list1, list2, order) { - if (order === void 0) { order = null; } - console.warn("deprecated!"); - if (order == null) { - order = function (x, y) { - if (x < y) { - return -1; - } - else if (x > y) { - return +1; - } - else /*(x == y)*/ { - return 0; - } - }; - } - if (list1.length <= 0) { - if (list2.length <= 0) { - return 0; - } - else { - return -1; - } - } - else { - if (list2.length <= 0) { - return +1; - } - else { - var result = order(list1[0], list2[0]); - if (result == 0) { - return lex(list1.slice(1), list2.slice(1), order); + function contrast(list_left, extract_key_left, list_right, extract_key_right) { + var gathering = {}; + list_left.forEach(function (source_left) { + var _a; + var key = extract_key_left(source_left); + gathering[key] = Object.assign(((_a = gathering[key]) !== null && _a !== void 0 ? _a : {}), { "left": source_left }); + }); + list_right.forEach(function (source_right) { + var _a; + var key = extract_key_right(source_right); + gathering[key] = Object.assign(((_a = gathering[key]) !== null && _a !== void 0 ? _a : {}), { "right": source_right }); + }); + var result = { + "both": [], + "only_left": [], + "only_right": [] + }; + Object.entries(gathering).forEach(function (_a) { + var key = _a[0], value = _a[1]; + if ("left" in value) { + if ("right" in value) { + result.both.push({ "key": key, "left": value.left, "right": value.right }); } else { - return result; + result.only_left.push({ "key": key, "left": value.left }); } } - } - } - list_1.lex = lex; - function lex_lt(list1, list2) { - return (lex(list1, list2) < 0); - } - list_1.lex_lt = lex_lt; - ; - function lex_gt(list1, list2) { - return (lex(list1, list2) > 0); - } - list_1.lex_gt = lex_gt; - ; - function lex_le(list1, list2) { - return (lex(list1, list2) <= 0); - } - list_1.lex_le = lex_le; - ; - function lex_ge(list1, list2) { - return (lex(list1, list2) >= 0); - } - list_1.lex_ge = lex_ge; - ; - function lex_eq(list1, list2) { - return (lex(list1, list2) == 0); - } - list_1.lex_eq = lex_eq; - ; - function lex_ne(list1, list2) { - return (lex(list1, list2) != 0); - } - list_1.lex_ne = lex_ne; - ; - /** - * @author fenris - */ - function filter_inplace(list, predicate) { - var index = 0; - while (index < list.length) { - var element = list[index]; - if (predicate(element, index)) { - index += 1; - } else { - list.splice(index, 1); + if ("right" in value) { + result.only_right.push({ "key": key, "right": value.right }); + } + else { + // impossible + // do nothing + } } - } + }); + return result; } - list_1.filter_inplace = filter_inplace; + list_1.contrast = contrast; })(list = lib_plankton.list || (lib_plankton.list = {})); })(lib_plankton || (lib_plankton = {})); -/** - * @deprecated - */ -var lib_list; -(function (lib_list) { - lib_list.range = lib_plankton.list.range; - lib_list.sequence = lib_plankton.list.sequence; - lib_list.from_structure = lib_plankton.list.from_structure; - lib_list.from_iterator = lib_plankton.list.from_iterator; - lib_list.empty = lib_plankton.list.empty; - lib_list.zip = lib_plankton.list.zip; - lib_list.equals = lib_plankton.list.equals; - lib_list.reversed = lib_plankton.list.reversed; - lib_list.sorted = lib_plankton.list.sorted; - lib_list.keep = lib_plankton.list.keep; - lib_list.drop = lib_plankton.list.drop; - lib_list.clean = lib_plankton.list.clean; - lib_list.clone = lib_plankton.list.clone; - lib_list.separate = lib_plankton.list.separate; - lib_list.split = lib_plankton.list.split; - lib_list.group = lib_plankton.list.group; - lib_list.groups_ = lib_plankton.list.groups_; - lib_list.groups = lib_plankton.list.groups; - lib_list.find = lib_plankton.list.find; - lib_list.has = lib_plankton.list.has; - lib_list.contains = lib_plankton.list.contains; - lib_list.max = lib_plankton.list.max; - lib_list.min = lib_plankton.list.min; - lib_list.maxarg = lib_plankton.list.maxarg; - lib_list.minarg = lib_plankton.list.minarg; - lib_list.distribute = lib_plankton.list.distribute; - lib_list.lex = lib_plankton.list.lex; - lib_list.lex_lt = lib_plankton.list.lex_lt; - lib_list.lex_gt = lib_plankton.list.lex_gt; - lib_list.lex_le = lib_plankton.list.lex_le; - lib_list.lex_ge = lib_plankton.list.lex_ge; - lib_list.lex_eq = lib_plankton.list.lex_eq; - lib_list.lex_ne = lib_plankton.list.lex_ne; - lib_list.filter_inplace = lib_plankton.list.filter_inplace; -})(lib_list || (lib_list = {})); /* This file is part of »bacterio-plankton:file«. @@ -2286,21 +2148,6 @@ var lib_plankton; email.send = send; })(email = lib_plankton.email || (lib_plankton.email = {})); })(lib_plankton || (lib_plankton = {})); -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); /* This file is part of »bacterio-plankton:log«. @@ -2335,6 +2182,31 @@ var lib_plankton; enum_level[enum_level["error"] = 4] = "error"; })(enum_level = log.enum_level || (log.enum_level = {})); ; + })(log = lib_plankton.log || (lib_plankton.log = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:log«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:log« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:lang« 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 +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:log«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var log; + (function (log) { /** */ function level_order(level1, level2) { @@ -2343,469 +2215,33 @@ var lib_plankton; log.level_order = level_order; /** */ - function level_show(level) { - switch (level) { - case enum_level.debug: return "debug"; - case enum_level.info: return "info"; - case enum_level.notice: return "notice"; - case enum_level.warning: return "warning"; - case enum_level.error: return "error"; - default: return "(unknown)"; + function level_show(level, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b["abbreviated"], option_abbreviated = _c === void 0 ? false : _c; + if (option_abbreviated) { + switch (level) { + case log.enum_level.debug: return "DBG"; + case log.enum_level.info: return "INF"; + case log.enum_level.notice: return "NTC"; + case log.enum_level.warning: return "WRN"; + case log.enum_level.error: return "ERR"; + default: return "(unknown)"; + } + } + else { + switch (level) { + case log.enum_level.debug: return "debug"; + case log.enum_level.info: return "info"; + case log.enum_level.notice: return "notice"; + case log.enum_level.warning: return "warning"; + case log.enum_level.error: return "error"; + default: return "(unknown)"; + } } } log.level_show = level_show; - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:log«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:log« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:lang« 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 -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:lang«. If not, see . - */ -/** - * @deprecated - * @todo remove - */ -var lib_plankton; -(function (lib_plankton) { - var log; - (function (log) { - /** - * @author fenris - */ - /*export*/ var level_stack = [0]; - function level_push(level) { level_stack.push(level); } - log.level_push = level_push; - function level_pop() { if (level_stack.length > 1) { - level_stack.pop(); - } } - log.level_pop = level_pop; - function level_get() { return level_stack.slice(-1)[0]; } - /* - export function level_inc() : void {level_push(level_get()+1);} - export function level_dec() : void {level_push(level_get()-1);} - */ - /** - * @author fenris - */ - var indent_stack = [0]; - function indent_push(indent) { indent_stack.push(indent); } - log.indent_push = indent_push; - function indent_pop() { if (indent_stack.length > 1) { - indent_stack.pop(); - } } - log.indent_pop = indent_pop; - function indent_get() { return level_stack.slice(-1)[0]; } - function indent_inc() { level_push(level_get() + 1); } - log.indent_inc = indent_inc; - function indent_dec() { level_push(level_get() - 1); } - log.indent_dec = indent_dec; - /** - * @author fenris - */ - function write(_a) { - var message = _a["message"], _b = _a["type"], type = _b === void 0 ? null : _b, _c = _a["prefix"], prefix = _c === void 0 ? null : _c, _d = _a["level"], level = _d === void 0 ? 0 : _d, _e = _a["indent"], indent = _e === void 0 ? 0 : _e; - var entry = { - "level": ((type === null) - ? lib_plankton.log.enum_level.info - : { - "debug": lib_plankton.log.enum_level.debug, - "info": lib_plankton.log.enum_level.info, - "notice": lib_plankton.log.enum_level.notice, - "warning": lib_plankton.log.enum_level.warning, - "error": lib_plankton.log.enum_level.error - }[type]), - "incident": message, - "details": { - "prefix": prefix, - "level": level, - "indent": indent - } - }; - lib_plankton.log.add(entry); - } - log.write = write; - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:log«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:log« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:lang« 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 -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:log«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var log; - (function (log) { /** */ - var class_channel = /** @class */ (function () { - function class_channel() { - } - return class_channel; - }()); - log.class_channel = class_channel; - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:log«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:log« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:lang« 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 -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:log«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var log; - (function (log) { - /** - * output for writing log entries to stdout - */ - var class_channel_stdout = /** @class */ (function (_super) { - __extends(class_channel_stdout, _super); - function class_channel_stdout() { - return _super !== null && _super.apply(this, arguments) || this; - } - /** - */ - class_channel_stdout.prototype.add = function (entry) { - process.stdout.write(("<" + (new Date(Date.now())).toISOString().slice(0, 19) + ">") - + - " " - + - ("[" + log.level_show(entry.level) + "]") - + - " " - + - ("" + entry.incident + "") - + - ": " - + - JSON.stringify(entry.details, undefined, " ") - + - "\n"); - }; - return class_channel_stdout; - }(log.class_channel)); - log.class_channel_stdout = class_channel_stdout; - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:log«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:log« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:lang« 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 -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:log«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var log; - (function (log) { - /** - */ - var class_channel_file = /** @class */ (function (_super) { - __extends(class_channel_file, _super); - /** - * [constructor] - */ - function class_channel_file(path, human_readable) { - var _this = _super.call(this) || this; - _this.path = path; - _this.human_readable = human_readable; - return _this; - } - /** - */ - class_channel_file.prototype.add = function (entry) { - var _this = this; - var nm_fs = require("fs"); - var line = (this.human_readable - ? (("<" + (new Date(Date.now())).toISOString().slice(0, 19) + ">") - + - " " - + - ("[" + log.level_show(entry.level) + "]") - + - " " - + - ("" + entry.incident + "") - + - ": " - + - JSON.stringify(entry.details, undefined, " ") - + - "\n") - : (JSON.stringify({ - "timestamp": lib_plankton.base.get_current_timestamp(), - "level_number": entry.level, - "level_name": log.level_show(entry.level), - "incident": entry.incident, - "details": entry.details - }) - + - "\n")); - nm_fs.writeFile(this.path, line, { - "flag": "a+" - }, function (error) { - if (error !== null) { - process.stderr.write('-- [plankton] could not add log entry to file ' + _this.path + "\n"); - } - else { - // do nothing - } - }); - }; - return class_channel_file; - }(log.class_channel)); - log.class_channel_file = class_channel_file; - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:log«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:log« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:lang« 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 -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:log«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var log; - (function (log) { - /** - */ - var class_channel_email = /** @class */ (function (_super) { - __extends(class_channel_email, _super); - /** - * [constructor] - */ - function class_channel_email(smtp_credentials, sender, receivers) { - var _this = _super.call(this) || this; - _this.smtp_credentials = smtp_credentials; - _this.sender = sender; - _this.receivers = receivers; - return _this; - } - /** - */ - class_channel_email.prototype.add = function (entry) { - var nm_fs = require("fs"); - lib_plankton.email.send(this.smtp_credentials, this.sender, this.receivers, (("[" + log.level_show(entry.level) + "]") - + - " " - + - ("" + entry.incident + "")), JSON.stringify(entry.details, undefined, " ")); - }; - return class_channel_email; - }(log.class_channel)); - log.class_channel_email = class_channel_email; - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:log«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:log« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:lang« 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 -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:log«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var log; - (function (log) { - /** - * output for desktop notifications via "libnotify" - */ - var class_channel_notify = /** @class */ (function (_super) { - __extends(class_channel_notify, _super); - function class_channel_notify() { - return _super !== null && _super.apply(this, arguments) || this; - } - /** - */ - class_channel_notify.prototype.add = function (entry) { - var nm_child_process = require("child_process"); - var command = ("notify-send" - + - " " - + - ("'" - + - ("[" + log.level_show(entry.level) + "]") - + - " " - + - entry.incident - + - "'") - + - " " - + - ("'" - + - (Object.keys(entry.details) - .map(function (key) { return (key + ": " + JSON.stringify(entry.details[key])); }) - .join("\n")) - + - "'")); - nm_child_process.exec(command, function (error, stdout, stderr) { - // do noting - }); - }; - return class_channel_notify; - }(log.class_channel)); - log.class_channel_notify = class_channel_notify; - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:log«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:log« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:lang« 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 -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:log«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var log; - (function (log) { - /** - * decorator for filtering out log entries below a certain level threshold - */ - var class_channel_minlevel = /** @class */ (function (_super) { - __extends(class_channel_minlevel, _super); - /** - */ - function class_channel_minlevel(core, threshold) { - var _this = _super.call(this) || this; - _this.core = core; - _this.threshold = threshold; - return _this; - } - /** - */ - class_channel_minlevel.prototype.add = function (entry) { - if (!log.level_order(this.threshold, entry.level)) { - // do nothing - } - else { - this.core.add(entry); - } - }; - return class_channel_minlevel; - }(log.class_channel)); - log.class_channel_minlevel = class_channel_minlevel; - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:log«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:log« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:lang« 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 -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:log«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var log; - (function (log) { - /** - */ - function translate_level(level_string) { + function level_decode(level_string) { return { "debug": log.enum_level.debug, "info": log.enum_level.info, @@ -2814,43 +2250,468 @@ var lib_plankton; "error": log.enum_level.error }[level_string]; } + log.level_decode = level_decode; + })(log = lib_plankton.log || (lib_plankton.log = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:log«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:log« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:lang« 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 +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:log«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var log; + (function (log) { + /** + * @todo use label + */ + function get_logger_logic(logger_data) { + return logger_data.map(function (channel_description) { return lib_plankton.log.get_channel_logic(channel_description); }); + } + log.get_logger_logic = get_logger_logic; /** */ - function channel_make(description) { - var _a, _b, _c, _d, _e; - switch (description.kind) { + function format_entry(format_definition, entry) { + switch (format_definition.kind) { + case "jsonl": { + var now = Date.now(); + var timestamp = (now / 1000); + var datetime = (new Date(now)).toISOString(); + return (JSON.stringify({ + "datetime_timestamp": Math.round(timestamp), + "datetime_string": datetime /*.slice(0, 19)*/, + "level_numeric": entry.level, + "level_name": log.level_show(entry.level, { "abbreviated": false }), + "tags": entry.tags, + "incident": entry.incident, + "details": entry.details + }, undefined, (format_definition.data.structured + ? + "\t" + : + undefined))); + break; + } + case "human_readable": { + var parts = []; + parts.push(("<" + (new Date(Date.now())).toISOString() /*.slice(0, 19)*/ + ">")); + parts.push(("[" + log.level_show(entry.level, { "abbreviated": true }) + "]")); + for (var _i = 0, _a = entry.tags; _i < _a.length; _i++) { + var tag = _a[_i]; + parts.push(("{" + tag + "}")); + } + parts.push(entry.incident); + (entry.details !== null) && parts.push((": " + JSON.stringify(entry.details, undefined, undefined))); + return (parts.join(" ")); + break; + } default: { - throw (new Error("unhandled log channel kind: " + description.kind)); - break; - } - case "stdout": { - return (new log.class_channel_minlevel(new log.class_channel_stdout(), translate_level((_a = description.data["threshold"]) !== null && _a !== void 0 ? _a : "debug"))); - break; - } - case "file": { - return (new log.class_channel_minlevel(new log.class_channel_file(((_b = description.data["path"]) !== null && _b !== void 0 ? _b : "/tmp/plankton.log"), false), translate_level((_c = description.data["threshold"]) !== null && _c !== void 0 ? _c : "debug"))); - break; - } - case "email": { - return (new log.class_channel_minlevel(new log.class_channel_email(description.data["smtp_credentials"], description.data["sender"], description.data["receivers"]), translate_level((_d = description.data["threshold"]) !== null && _d !== void 0 ? _d : "debug"))); - break; - } - case "notify": { - return (new log.class_channel_minlevel(new log.class_channel_notify(), translate_level((_e = description.data["threshold"]) !== null && _e !== void 0 ? _e : "debug"))); + throw (new Error("unhandled format kind: " + format_definition["kind"])); break; } } } - log.channel_make = channel_make; + log.format_entry = format_entry; /** */ - function conf_default() { - return [ - new log.class_channel_minlevel(new log.class_channel_stdout(), log.enum_level.notice), - new log.class_channel_minlevel(new log.class_channel_notify(), log.enum_level.error), - ]; + function parse_format_definition(format_definition_raw) { + return lib_plankton.call.distinguish((format_definition_raw !== null && format_definition_raw !== void 0 ? format_definition_raw : { + "kind": "human_readable", + "data": {} + }), { + "jsonl": function (_a) { + var structured = _a["structured"]; + return ({ + "kind": "jsonl", + "data": { + "structured": (structured !== null && structured !== void 0 ? structured : false) + } + }); + }, + "human_readable": function (data_) { return ({ + "kind": "human_readable", + "data": {} + }); } + }); } - log.conf_default = conf_default; + log.parse_format_definition = parse_format_definition; + })(log = lib_plankton.log || (lib_plankton.log = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:log«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:log« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:lang« 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 +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:log«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var log; + (function (log) { + var channel; + (function (channel) { + var filtered; + (function (filtered) { + /** + */ + function predicate_incident(substring) { + return (function (entry) { return entry.incident.includes(substring); }); + } + filtered.predicate_incident = predicate_incident; + /** + */ + function predicate_level(threshold) { + return (function (entry) { return log.level_order(threshold, entry.level); }); + } + filtered.predicate_level = predicate_level; + /** + */ + function predicate_tag(tag) { + return (function (entry) { return entry.tags.includes(tag); }); + } + filtered.predicate_tag = predicate_tag; + /** + * combines other predicates in disjunctive normal form + */ + function predicate_complex(definition) { + return (function (entry) { return definition.some(function (clause) { return clause.every(function (literal) { return (literal.item(entry) + === + literal.mode); }); }); }); + } + filtered.predicate_complex = predicate_complex; + /** + */ + function send(subject, entry) { + if (!subject.predicate(entry)) { + // do nothing + } + else { + subject.core.send(entry); + } + } + filtered.send = send; + /** + */ + function logic(subject) { + return { + "send": function (entry) { return send(subject, entry); } + }; + } + filtered.logic = logic; + })(filtered = channel.filtered || (channel.filtered = {})); + })(channel = log.channel || (log.channel = {})); + })(log = lib_plankton.log || (lib_plankton.log = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:log«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:log« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:lang« 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 +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:log«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var log; + (function (log) { + var channel; + (function (channel) { + var minlevel; + (function (minlevel) { + /** + */ + function to_filter_subject(subject) { + return { + "core": subject.core, + "predicate": lib_plankton.log.channel.filtered.predicate_level(subject.threshold) + }; + } + /** + */ + function send(subject, entry) { + lib_plankton.log.channel.filtered.send(to_filter_subject(subject), entry); + } + minlevel.send = send; + /** + */ + function logic(subject) { + return { + "send": function (entry) { return send(subject, entry); } + }; + } + minlevel.logic = logic; + })(minlevel = channel.minlevel || (channel.minlevel = {})); + })(channel = log.channel || (log.channel = {})); + })(log = lib_plankton.log || (lib_plankton.log = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:log«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:log« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:lang« 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 +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:log«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var log; + (function (log) { + var channel; + (function (channel) { + var std; + (function (std) { + /** + */ + function send(subject, entry) { + var write = lib_plankton.call.distinguish({ + "kind": subject.target, + "data": null + }, { + "stdout": function () { return function (x) { return process.stdout.write(x); }; }, + "stderr": function () { return function (x) { return process.stderr.write(x); }; } + }); + write(lib_plankton.log.format_entry(subject.format, entry) + + + "\n"); + } + std.send = send; + /** + */ + function logic(subject) { + return { + "send": function (entry) { return send(subject, entry); } + }; + } + std.logic = logic; + })(std = channel.std || (channel.std = {})); + })(channel = log.channel || (log.channel = {})); + })(log = lib_plankton.log || (lib_plankton.log = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:log«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:log« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:lang« 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 +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:log«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var log; + (function (log) { + var channel; + (function (channel) { + var file; + (function (file) { + /** + */ + function send(subject, entry) { + var _this = this; + var nm_fs = require("fs"); + nm_fs.writeFile(subject.path, (lib_plankton.log.format_entry(subject.format, entry) + + + "\n"), { + "flag": "a+" + }, function (error) { + if (error !== null) { + process.stderr.write('-- [plankton] could not add log entry to file ' + _this.path + "\n"); + } + else { + // do nothing + } + }); + } + file.send = send; + /** + */ + function logic(subject) { + return { + "send": function (entry) { return send(subject, entry); } + }; + } + file.logic = logic; + })(file = channel.file || (channel.file = {})); + })(channel = log.channel || (log.channel = {})); + })(log = lib_plankton.log || (lib_plankton.log = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:log«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:log« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:lang« 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 +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:log«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var log; + (function (log) { + var channel; + (function (channel) { + var notify; + (function (notify) { + /** + * @todo tags + */ + function send(subject, entry) { + var nm_child_process = require("child_process"); + var command = ("notify-send" + + + " " + + + ("'" + + + ("[" + log.level_show(entry.level) + "]") + + + " " + + + entry.incident + + + "'") + + + " " + + + ("'" + + + JSON.stringify(entry.details) + + + "'")); + nm_child_process.exec(command, function (error, stdout, stderr) { + // do noting + }); + } + notify.send = send; + /** + */ + function logic(subject) { + return { + "send": function (entry) { return send(subject, entry); } + }; + } + notify.logic = logic; + })(notify = channel.notify || (channel.notify = {})); + })(channel = log.channel || (log.channel = {})); + })(log = lib_plankton.log || (lib_plankton.log = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:log«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:log« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:lang« 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 +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:log«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var log; + (function (log) { + var channel; + (function (channel) { + var email; + (function (email) { + /** + * @todo tags + */ + function send(subject, entry) { + var nm_fs = require("fs"); + lib_plankton.email.send(subject.smtp_credentials, subject.sender, subject.receivers, (("[" + log.level_show(entry.level) + "]") + + + " " + + + ("" + entry.incident + "")), JSON.stringify(entry.details, undefined, " ")); + } + email.send = send; + /** + */ + function logic(subject) { + return { + "send": function (entry) { return send(subject, entry); } + }; + } + email.logic = logic; + })(email = channel.email || (channel.email = {})); + })(channel = log.channel || (log.channel = {})); })(log = lib_plankton.log || (lib_plankton.log = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -2878,82 +2739,402 @@ var lib_plankton; (function (log) { /** */ - var _channel_stack = null; - /** - * pushes a new configuration on the stack and activates it - */ - function conf_push(channels) { - if (_channel_stack === null) { - _channel_stack = []; - } - _channel_stack.push(channels); - } - log.conf_push = conf_push; - /** - * pops the current active configuration from the stack - */ - function conf_pop() { - if (_channel_stack.length > 0) { - _channel_stack.pop(); - } - else { - // do nothing + 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")); + } + 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; + } } } - log.conf_pop = conf_pop; + log.get_channel_logic = get_channel_logic; + })(log = lib_plankton.log || (lib_plankton.log = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:log«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:log« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:lang« 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 +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:log«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var log; + (function (log) { /** - * makes the logging system ready */ - function setup() { - if (_channel_stack === null) { - _channel_stack = []; - conf_push(log.conf_default()); - } - else { - // do nothing - } + function default_logger() { + return [ + { + "kind": "minlevel", + "data": { + "core": { + "kind": "std", + "data": { + "target": "stdout", + "format": { + "kind": "human_readable", + "data": {} + } + } + }, + "threshold": "info" + } + }, + ]; + } + log.default_logger = default_logger; + })(log = lib_plankton.log || (lib_plankton.log = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:log«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:log« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:lang« 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 +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:log«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var log; + (function (log) { + /** + */ + var _main_logger_data = null; + /** + */ + function set_main_logger(logger_data) { + _main_logger_data = logger_data; + } + log.set_main_logger = set_main_logger; + /** + */ + function get_main_logger() { + return (_main_logger_data !== null && _main_logger_data !== void 0 ? _main_logger_data : log.default_logger()); } /** - * consumes a log entry, i.e. sends it to the currently active outputs */ - function add(entry) { - setup(); - _channel_stack.slice(-1)[0].forEach(function (channel) { return channel.add(entry); }); + function get_main_logger_logic() { + return lib_plankton.log.get_logger_logic(get_main_logger()); } - log.add = add; /** + * consumes a log entry, i.e. sends it to all channels */ - function debug(incident, details) { - if (details === void 0) { details = {}; } - add({ "level": log.enum_level.debug, "incident": incident, "details": details }); + function send_(logger, entry) { + logger.forEach(function (channel) { return channel.send(entry); }); + } + log.send_ = send_; + /** + * [convenience] + * + * @todo rename to "send" + */ + function debug_(logger, incident, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b["tags"], option_tags = _c === void 0 ? [] : _c, _d = _b["details"], option_details = _d === void 0 ? null : _d; + send_(logger, { + "level": log.enum_level.debug, + "incident": incident, + "tags": option_tags, + "details": option_details + }); + } + log.debug_ = debug_; + /** + * [convenience] + * + * @todo rename to "info" + */ + function info_(logger, incident, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b["tags"], option_tags = _c === void 0 ? [] : _c, _d = _b["details"], option_details = _d === void 0 ? null : _d; + send_(logger, { + "level": log.enum_level.info, + "incident": incident, + "tags": option_tags, + "details": option_details + }); + } + log.info_ = info_; + /** + * [convenience] + * + * @todo rename to "notice" + */ + function notice_(logger, incident, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b["tags"], option_tags = _c === void 0 ? [] : _c, _d = _b["details"], option_details = _d === void 0 ? null : _d; + send_(logger, { + "level": log.enum_level.notice, + "incident": incident, + "tags": option_tags, + "details": option_details + }); + } + log.notice_ = notice_; + /** + * [convenience] + * + * @todo rename to "warning" + */ + function warning_(logger, incident, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b["tags"], option_tags = _c === void 0 ? [] : _c, _d = _b["details"], option_details = _d === void 0 ? null : _d; + send_(logger, { + "level": log.enum_level.warning, + "incident": incident, + "tags": option_tags, + "details": option_details + }); + } + log.warning_ = warning_; + /** + * [convenience] + * + * @todo rename to "error" + */ + function error_(logger, incident, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b["tags"], option_tags = _c === void 0 ? [] : _c, _d = _b["details"], option_details = _d === void 0 ? null : _d; + send_(logger, { + "level": log.enum_level.error, + "incident": incident, + "tags": option_tags, + "details": option_details + }); + } + log.error_ = error_; + /** + * [convenience] + */ + function _send(entry) { + send_(get_main_logger_logic(), entry); + } + log._send = _send; + /** + * [convenience] + */ + function _debug(incident, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b["tags"], option_tags = _c === void 0 ? [] : _c, _d = _b["details"], option_details = _d === void 0 ? null : _d; + debug_(get_main_logger_logic(), incident, { + "tags": option_tags, + "details": option_details + }); + } + log._debug = _debug; + /** + * [convenience] + */ + function _info(incident, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b["tags"], option_tags = _c === void 0 ? [] : _c, _d = _b["details"], option_details = _d === void 0 ? null : _d; + info_(get_main_logger_logic(), incident, { + "tags": option_tags, + "details": option_details + }); + } + log._info = _info; + /** + * [convenience] + */ + function _notice(incident, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b["tags"], option_tags = _c === void 0 ? [] : _c, _d = _b["details"], option_details = _d === void 0 ? null : _d; + notice_(get_main_logger_logic(), incident, { + "tags": option_tags, + "details": option_details + }); + } + log._notice = _notice; + /** + * [convenience] + */ + function _warning(incident, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b["tags"], option_tags = _c === void 0 ? [] : _c, _d = _b["details"], option_details = _d === void 0 ? null : _d; + warning_(get_main_logger_logic(), incident, { + "tags": option_tags, + "details": option_details + }); + } + log._warning = _warning; + /** + * [convenience] + */ + function _error(incident, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b["tags"], option_tags = _c === void 0 ? [] : _c, _d = _b["details"], option_details = _d === void 0 ? null : _d; + error_(get_main_logger_logic(), incident, { + "tags": option_tags, + "details": option_details + }); + } + log._error = _error; + /** + * [convenience] + * + * @deprecated use ._debug instead! + */ + function debug(incident, details, tags) { + if (details === void 0) { details = null; } + if (tags === void 0) { tags = []; } + _debug(incident, { + "details": details, + "tags": tags + }); } log.debug = debug; /** + * [convenience] + * + * @deprecated use ._info instead! */ - function info(incident, details) { - if (details === void 0) { details = {}; } - add({ "level": log.enum_level.info, "incident": incident, "details": details }); + function info(incident, details, tags) { + if (details === void 0) { details = null; } + if (tags === void 0) { tags = []; } + _info(incident, { + "details": details, + "tags": tags + }); } log.info = info; /** + * [convenience] + * + * @deprecated use ._notice instead! */ - function notice(incident, details) { - if (details === void 0) { details = {}; } - add({ "level": log.enum_level.notice, "incident": incident, "details": details }); + function notice(incident, details, tags) { + if (details === void 0) { details = null; } + if (tags === void 0) { tags = []; } + _notice(incident, { + "details": details, + "tags": tags + }); } log.notice = notice; /** + * [convenience] + * + * @deprecated use ._warning instead! */ - function warning(incident, details) { - if (details === void 0) { details = {}; } - add({ "level": log.enum_level.warning, "incident": incident, "details": details }); + function warning(incident, details, tags) { + if (details === void 0) { details = null; } + if (tags === void 0) { tags = []; } + _warning(incident, { + "details": details, + "tags": tags + }); } log.warning = warning; /** + * [convenience] + * + * @deprecated use ._error instead! */ - function error(incident, details) { - if (details === void 0) { details = {}; } - add({ "level": log.enum_level.error, "incident": incident, "details": details }); + function error(incident, details, tags) { + if (details === void 0) { details = null; } + if (tags === void 0) { tags = []; } + _error(incident, { + "details": details, + "tags": tags + }); } log.error = error; })(log = lib_plankton.log || (lib_plankton.log = {})); @@ -3167,6 +3348,7 @@ var lib_plankton; */ function generate(prefix = "string_") { if (index_is > index_max) { + lib_plankton.log.error("plankton.string.generate.out_of_valid_indices", null); throw (new Error("[string_generate] out of valid indices")); } else { @@ -3193,7 +3375,7 @@ var lib_plankton; * @return {Array} * @author fenris */ - function split(chain, separator = " ") { + function split(chain, separator) { if (chain.length == 0) { return []; } @@ -3397,11 +3579,16 @@ var lib_plankton; if (options.legacy) { const value = args[key]; const regexp_argument = new RegExp("\\${" + key + "}", "g"); - lib_plankton.log.debug("lib_plankton.string.coin", { - "key": key, - "regex": regexp_argument.toString(), - "value": value, - }); + /* + lib_plankton.log.debug( + "lib_plankton.string.coin", + { + "key": key, + "regex": regexp_argument.toString(), + "value": value, + } + ); + */ str = str.replace(regexp_argument, value); } } @@ -3409,11 +3596,16 @@ var lib_plankton; { const value = args[key]; const regexp_argument = new RegExp(options.open + key + options.close, "g"); - lib_plankton.log.debug("lib_plankton.string.coin", { - "key": key, - "regex": regexp_argument.toString(), - "value": value, - }); + /* + lib_plankton.log.debug( + "lib_plankton.string.coin", + { + "key": key, + "regex": regexp_argument.toString(), + "value": value, + } + ); + */ str = str.replace(regexp_argument, value); } }); @@ -3457,6 +3649,12 @@ var lib_plankton; return slices; } string.slice = slice; + /** + */ + function capitalize(str) { + return (str[0].toUpperCase() + str.slice(1)); + } + string.capitalize = capitalize; })(string = lib_plankton.string || (lib_plankton.string = {})); })(lib_plankton || (lib_plankton = {})); /** @@ -5775,6 +5973,107 @@ var lib_plankton; })(code = lib_plankton.code || (lib_plankton.code = {})); })(lib_plankton || (lib_plankton = {})); /* +This file is part of »bacterio-plankton:www_form«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:www_form« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:www_form« 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 +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:www_form«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var www_form; + (function (www_form) { + /** + * @author fenris + */ + function encode(source) { + return (Object.entries(source) + .map(function (_a) { + var key = _a[0], value = _a[1]; + return (key + "=" + encodeURIComponent(value)); + }) + .join("&")); + } + www_form.encode = encode; + /** + * @author fenris + */ + function decode(target) { + return (Object.fromEntries(target.split("&") + .map(function (part) { + var components = part.split("="); + var key = components[0]; + var value = decodeURIComponent(components.slice(1).join("=")); + return [key, value]; + }))); + } + www_form.decode = decode; + })(www_form = lib_plankton.www_form || (lib_plankton.www_form = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:www_form«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:www_form« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:www_form« 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 +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:www_form«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var www_form; + (function (www_form) { + /** + * @author fenris + */ + var class_www_form = /** @class */ (function () { + /** + * @author fenris + */ + function class_www_form() { + } + /** + * @implementation + * @author fenris + */ + class_www_form.prototype.encode = function (source) { + return www_form.encode(source); + }; + /** + * @implementation + * @author fenris + */ + class_www_form.prototype.decode = function (target) { + return www_form.decode(target); + }; + return class_www_form; + }()); + www_form.class_www_form = class_www_form; + })(www_form = lib_plankton.www_form || (lib_plankton.www_form = {})); +})(lib_plankton || (lib_plankton = {})); +/* This file is part of »bacterio-plankton:url«. Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' @@ -5821,23 +6120,23 @@ var lib_plankton; */ function encode(url) { let result = ""; - // protocol + // scheme { - if (url.hasOwnProperty("protocol") && (url.protocol !== null)) { - result += (url.protocol + ":"); + if (url.scheme !== null) { + result += (url.scheme + ":"); } } // host { - if (url.hasOwnProperty("host") && (url.host !== null)) { + if (url.host !== null) { result += "//"; // username { - if (url.hasOwnProperty("username") && (url.username !== null)) { + if (url.username !== null) { result += url.username; // password { - if (url.hasOwnProperty("password") && (url.password !== null)) { + if (url.password !== null) { result += (":" + url.password); } } @@ -5849,30 +6148,25 @@ var lib_plankton; } // port { - if (url.hasOwnProperty("port") && (url.port !== null)) { + if (url.port !== null) { result += (":" + url.port.toString()); } } // path { - if (url.hasOwnProperty("path") && (url.path !== null)) { - const path = encodeURI(url.path.join("/")); - result += ("/" + path); + if (url.path !== null) { + result += url.path; } } - // arguments + // query { - if (url.hasOwnProperty("arguments") && (url.arguments !== null)) { - const search = (Object.entries(url.arguments) - .map(([key, value]) => (key + "=" + value)) - .join("&")); - const search_ = encodeURI(search); - result += ("?" + search_); + if (url.query !== null) { + result += ("?" + url.query); } } // hash { - if (url.hasOwnProperty("hash") && (url.hash !== null)) { + if (url.hash !== null) { result += ("#" + url.hash); } } @@ -5886,24 +6180,30 @@ var lib_plankton; function decode(url_raw) { const builtin_url = new URL(url_raw); return { - "protocol": builtin_url.protocol.slice(0, -1), + "scheme": builtin_url.protocol.slice(0, -1), "host": builtin_url.hostname, - "username": builtin_url.username, - "password": builtin_url.password, - "port": parseInt(builtin_url.port), - "path": builtin_url.pathname.slice(1).split("/"), - "hash": builtin_url.hash.slice(1), - "arguments": (() => { - let arguments_ = {}; - const search_parts = builtin_url.search.slice(1).split("&"); - search_parts.forEach((search_part) => { - const components = search_part.split("=", 2); - const key = components[0]; - const value = components[1]; - arguments_[key] = value; - }); - return arguments_; - })(), + "username": ((builtin_url.username !== "") + ? + builtin_url.username + : + null), + "password": ((builtin_url.password !== "") + ? + builtin_url.password + : + null), + "port": ((builtin_url.port !== "") + ? + parseInt(builtin_url.port) + : + null), + "path": builtin_url.pathname, + "query": builtin_url.search.slice(1), + "hash": ((builtin_url.hash !== "") + ? + builtin_url.hash.slice(1) + : + null), }; } url_1.decode = decode; @@ -6489,7 +6789,7 @@ var lib_plankton; */ function generate_url() { return (lib_plankton.url.encode({ - "protocol": choose_uniformly([ + "scheme": choose_uniformly([ "http", "https", "git", @@ -6497,17 +6797,23 @@ var lib_plankton; "ftp", ]), "host": generate_domain(), + "username": null, + "password": null, "port": generate_integer({ "minimum": 20, "maximum": 99999 }), - "path": (sequence(generate_integer({ "minimum": 0, "maximum": 3 })) - .map(function () { return generate_word(); })), - "arguments": choose_uniformly([ + "path": ("/" + + + (sequence(generate_integer({ "minimum": 0, "maximum": 3 })) + .map(function () { return generate_word(); }) + .join("/"))), + "query": choose_uniformly([ null, - Object.fromEntries(sequence(generate_integer({ "minimum": 0, "maximum": 3 })) + lib_plankton.www_form.encode(Object.fromEntries(sequence(generate_integer({ "minimum": 0, "maximum": 3 })) .map(function () { return ([ generate_word(), generate_integer().toFixed(0), - ]); })), - ]) + ]); }))), + ]), + "hash": null })); } random.generate_url = generate_url; @@ -7391,7 +7697,7 @@ var lib_plankton; /** */ function postgresql_wrap_name(name) { - return ("" + name + ""); + return ("\"" + name + "\""); } database.postgresql_wrap_name = postgresql_wrap_name; /** @@ -8989,17 +9295,30 @@ var lib_plankton; /** * @author fenris */ - function encode(x, formatted = false) { - return JSON.stringify(x, undefined, formatted ? "\t" : undefined); + function encode(source, options = {}) { + options = Object.assign({ + "formatted": false, + }, options); + return JSON.stringify(source, undefined, (options.formatted ? "\t" : undefined)); } json.encode = encode; /** * @author fenris */ - function decode(x) { - return JSON.parse(x); + function decode(target) { + return JSON.parse(target); } json.decode = decode; + /** + * @author fenris + */ + function implementation_code() { + return { + "encode": x => encode(x), + "decode": decode, + }; + } + json.implementation_code = implementation_code; })(json = lib_plankton.json || (lib_plankton.json = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -9077,128 +9396,136 @@ var lib_plankton; (function (object_1) { /** * @author fenris + * @deprecated use the "??" operator instead */ - function fetch(object, fieldname, fallback, escalation) { - if (fallback === void 0) { fallback = null; } - if (escalation === void 0) { escalation = 1; } - if ((fieldname in object) && (object[fieldname] !== undefined)) { + function fetch(object, fieldname, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "fallback": null, + "escalation": 1 + }, options); + if ((fieldname in object) + && + (object[fieldname] !== undefined)) { return object[fieldname]; } else { - switch (escalation) { - case 0: { - return fallback; - break; - } - case 1: { - var message = ("field '".concat(fieldname, "' not in structure")); - message += ("; using fallback value '".concat(String(fallback), "'")); - // console.warn(message); - return fallback; - break; - } - case 2: { - var message = ("field '".concat(fieldname, "' not in structure")); - throw (new Error(message)); - break; - } - default: { - throw (new Error("invalid escalation level ".concat(escalation))); - break; - } + if (!options.escalate) { + return options.fallback; + } + else { + throw (new Error("field '" + fieldname + "' not in structure")); } } } object_1.fetch = fetch; /** - * @author fenris */ function map(object_from, transformator) { - var object_to = {}; - Object.keys(object_from).forEach(function (key) { return (object_to[key] = transformator(object_from[key], key)); }); - return object_to; + return (Object.fromEntries(Object.entries(object_from) + .map(function (_a) { + var key = _a[0], value = _a[1]; + return ([key, transformator(value, key)]); + }))); } object_1.map = map; /** - * @desc gibt ein Objekt mit bestimmten Einträgen des Eingabe-Objekts zurück - * @author fenris + * gibt ein Objekt mit bestimmten Einträgen des Eingabe-Objekts zurück */ function filter(object_from, predicate) { - var object_to = {}; - Object.keys(object_from).forEach(function (key) { - var value = object_from[key]; - if (predicate(value, key)) { - object_to[key] = value; - } - }); - return object_to; + return (Object.fromEntries(Object.entries(object_from) + .filter(function (_a) { + var key = _a[0], value = _a[1]; + return predicate(value, key); + }))); } object_1.filter = filter; /** - * @desc wandelt ein Array mit Einträgen der Form {key,value} in ein entsprechendes Objekt um - * @author fenris + * wandelt ein Array mit Einträgen der Form {key,value} in ein entsprechendes Objekt um + * + * @deprecated use Object.fromEntries instead! */ function from_array(array) { - var object = {}; - array.forEach(function (entry) { return (object[entry.key] = entry.value); }); - return object; + return (Object.fromEntries(array + .map(function (_a) { + var key = _a["key"], value = _a["value"]; + return ([key, value]); + }))); } object_1.from_array = from_array; /** - * @desc wandelt ein Objekt in ein entsprechendes Array mit Einträgen der Form {key,value} um - * @author fenris + * wandelt ein Objekt in ein entsprechendes Array mit Einträgen der Form {key,value} um + * + * @deprecated use Object.entries insetad! */ function to_array(object) { - var array = []; - Object.keys(object).forEach(function (key) { return array.push({ "key": key, "value": object[key] }); }); - return array; + return (Object.entries(object) + .map(function (_a) { + var key = _a[0], value = _a[1]; + return ({ "key": key, "value": value }); + })); } object_1.to_array = to_array; /** - * @desc gibt eine Liste von Schlüsseln eines Objekts zurück - * @author fenris + * gibt eine Liste von Schlüsseln eines Objekts zurück + * + * @deprecated use Object.keys instead! */ function keys(object) { return Object.keys(object); } object_1.keys = keys; /** - * @desc gibt eine Liste von Werten eines Objekts zurück - * @author fenris + * gibt eine Liste von Werten eines Objekts zurück + * + * @deprecated use Object.values instead! */ function values(object) { - return to_array(object).map(function (entry) { return entry.value; }); + return Object.values(object); } object_1.values = values; /** - * @desc liest ein Baum-artiges Objekt an einer bestimmten Stelle aus - * @author fenris + * liest ein Baum-artiges Objekt an einer bestimmten Stelle aus */ - function path_read(object, path, fallback, escalation) { - if (fallback === void 0) { fallback = null; } - if (escalation === void 0) { escalation = 1; } + function path_read(object, path, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "fallback": null, + "escalate": false + }, options); var steps = ((path.length == 0) ? [] : path.split(".")); if (steps.length == 0) { throw (new Error("empty path")); } else { var position_1 = object; - var reachable = (position_1 != null) && steps.slice(0, steps.length - 1).every(function (step) { - position_1 = lib_plankton.object.fetch(position_1, step, null, 0); - return (position_1 != null); - }); + var reachable = ((position_1 != null) + && + (steps.slice(0, steps.length - 1) + .every(function (step) { + position_1 = lib_plankton.object.fetch(position_1, step, { + "fallback": null, + "escalate": false + }); + return (position_1 != null); + }))); if (reachable) { - return lib_plankton.object.fetch(position_1, steps[steps.length - 1], fallback, escalation); + return lib_plankton.object.fetch(position_1, steps[steps.length - 1], { + "fallback": options.fallback, + "escalate": options.escalate + }); } else { - return lib_plankton.object.fetch({}, "_dummy_", fallback, escalation); + return lib_plankton.object.fetch({}, "_dummy_", { + "fallback": options.fallback, + "escalate": options.escalate + }); } } } object_1.path_read = path_read; /** - * @desc schreibt einen Wert an eine bestimmte Stelle in einem Baum-artigen Objekt - * @author fenris + * schreibt einen Wert an eine bestimmte Stelle in einem Baum-artigen Objekt */ function path_write(object, path, value, construct) { if (construct === void 0) { construct = true; } @@ -9209,7 +9536,10 @@ var lib_plankton; else { var position_2 = object; var reachable = steps.slice(0, steps.length - 1).every(function (step) { - var position_ = lib_plankton.object.fetch(position_2, step, null, 0); + var position_ = lib_plankton.object.fetch(position_2, step, { + "fallback": null, + "escalate": false + }); if (position_ == null) { if (construct) { position_2[step] = {}; @@ -9229,99 +9559,111 @@ var lib_plankton; position_2[steps[steps.length - 1]] = value; } else { - var message = ("path '".concat(path, "' does not exist and may not be constructed")); - throw (new Error(message)); + throw (new Error("path '" + path + "' does not exist and may not be constructed")); } } } object_1.path_write = path_write; /** - * @desc prüft ob ein Objekt einem bestimmten Muster entspricht - * @param {Object} object das zu prüfende Objekt - * @param {Object} pattern das einzuhaltende Muster - * @param {Function} connlate eine Funktion zum Feststellen der Gleichheit von Einzelwerten - * @author fenris + * prüft ob ein Objekt einem bestimmten Muster entspricht + * + * @deprecated not very useful */ - function matches(object, pattern, collate) { - if (collate === void 0) { collate = instance_collate; } - return Object.keys(pattern).every(function (key) { return collate(pattern[key], object[key]); }); + function matches(object, pattern, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "collate": instance_collate + }, options); + return (Object.entries(pattern) + .every(function (_a) { + var key = _a[0], value = _a[1]; + return options.collate(value, object[key]); + })); } object_1.matches = matches; /** - * @desc erzeugt eine Projektion eines Baum-artigen Objekts in ein Listen-artiges Objekt - * @param {string} [separator] welches Zeichen als Trenner zwischen zwei Pfad-Schritten verwendet werden soll - * @author fenris + * erzeugt eine Projektion eines Baum-artigen Objekts in ein Listen-artiges Objekt */ - function flatten(value, separator, key_for_element) { - if (separator === void 0) { separator = "."; } - if (key_for_element === void 0) { key_for_element = (function (index) { return ("element_" + index.toFixed(0)); }); } - var integrate = function (result, key_, value_) { - if (value_ == null) { - result[key_] = value_; + function flatten(value, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "separator": ".", + "key_for_array_element": (function (index) { return ("element_" + index.toFixed(0)); }) + }, options); + var integrate = function (result, key, value) { + if (value == null) { + result[key] = value; } else { // primitive Werte direkt übernehmen - if (typeof (value_) != "object") { - result[key_] = value_; + if (typeof (value) != "object") { + result[key] = value; } // sonst durch rekursiven Aufruf die flache Variante des Wertes ermitteln und einarbeiten else { - var result_1 = flatten(value_); - Object.keys(result_1) - .forEach(function (key__) { - var value__ = result_1[key__]; - var key_new = (key_ + separator + key__); - result[key_new] = value__; + var result_ = flatten(value, { + "separator": options.separator, + "key_for_array_element": options.key_for_array_element + }); + Object.entries(result_).forEach(function (_a) { + var key_ = _a[0], value_ = _a[1]; + result[(key + options.separator + key_)] = value_; }); } } }; - if ((value === null) || (value === undefined)) { + if ((value === null) + || + (value === undefined)) { return null; } else { - var result_2 = {}; + var result_1 = {}; if (typeof (value) != "object") { - result_2["value"] = value; + result_1["value"] = value; } else { if (value instanceof Array) { - var array = (value); - array - .forEach(function (element, index) { - integrate(result_2, key_for_element(index), element); + value.forEach(function (element, index) { + integrate(result_1, options.key_for_array_element(index), element); }); } else { - var object_2 = (value); - Object.keys(object_2) - .forEach(function (key) { - integrate(result_2, key, object_2[key]); + Object.entries(value).forEach(function (_a) { + var key = _a[0], value = _a[1]; + integrate(result_1, key, value); }); } } - return result_2; + return result_1; } } object_1.flatten = flatten; /** - * @author fenris + * @deprecated use Object.assign instead! */ - function clash(x, y, _a) { - var _b = _a === void 0 ? {} : _a, _c = _b["overwrite"], overwrite = _c === void 0 ? true : _c, _d = _b["hooks"], _e = _d === void 0 ? {} : _d, _f = _e["existing"], hook_existing = _f === void 0 ? null : _f; - if (hook_existing == null) { - (function (key, value_old, value_new) { return console.warn("field ".concat(key, " already defined")); }); - } + function clash(x, y, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "overwrite": true, + "hooks": { + "existing": function (key, value_old, value_new) { + lib_plankton.log.warning("object_clash_field_already_defined", { + "key": key + }); + } + } + }, options); var z = {}; Object.keys(x).forEach(function (key) { z[key] = x[key]; }); Object.keys(y).forEach(function (key) { if (key in z) { - if (hook_existing != null) { - hook_existing(key, z[key], y[key]); + if (options.hooks.existing != null) { + options.hooks.existing(key, z[key], y[key]); } - if (overwrite) { + if (options.overwrite) { z[key] = y[key]; } } @@ -9333,24 +9675,39 @@ var lib_plankton; } object_1.clash = clash; /** - * @author fenris + * @deprecated use Object.assign instead! */ - function patch(core, mantle, deep, path) { - if (deep === void 0) { deep = true; } - if (path === void 0) { path = null; } + function patch(core, mantle, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "deep": true, + "path": null + }, options); if (mantle == null) { - console.warn("mantle is null; core was", core); + lib_plankton.log.warning("object_patch_mantle_is_null", { + "core": core + }); } else { Object.keys(mantle).forEach(function (key) { - var path_ = ((path == null) ? key : "".concat(path, ".").concat(key)); + var path_ = ((options.path == null) + ? + key + : + (options.path + "." + key)); var value_mantle = mantle[key]; if (!(key in core)) { - if ((typeof (value_mantle) == "object") && (value_mantle != null) && deep) { + if ((typeof (value_mantle) == "object") + && + (value_mantle != null) + && + options.deep) { if (value_mantle instanceof Array) { core[key] = []; value_mantle.forEach(function (element) { - if ((typeof (element) == "object") && (element != null)) { + if ((typeof (element) == "object") + && + (element != null)) { var element_ = {}; patch(element_, element); core[key].push(element_); @@ -9362,7 +9719,10 @@ var lib_plankton; } else { core[key] = {}; - patch(core[key], value_mantle, deep, path_); + patch(core[key], value_mantle, { + "deep": options.deep, + "path": path_ + }); } } else { @@ -9372,17 +9732,29 @@ var lib_plankton; else { var value_core = core[key]; if (typeof (value_core) == typeof (value_mantle)) { - if ((typeof (value_mantle) == "object") && (value_mantle != null) && deep) { - patch(core[key], value_mantle, deep, path_); + if ((typeof (value_mantle) == "object") + && + (value_mantle != null) + && + options.deep) { + patch(core[key], value_mantle, { + "deep": options.deep, + "path": path_ + }); } else { core[key] = value_mantle; } } else { - if ((value_core != null) && (value_mantle != null)) { - var message = "objects have different shapes at path '".concat(path_, "'; core has type '").concat(typeof (value_core), "' and mantle has type '").concat(typeof (value_mantle), "'"); - console.warn(message); + if ((value_core != null) + && + (value_mantle != null)) { + lib_plankton.log.warning("object_path_different_shapes", { + "path": path_, + "core_type": typeof (value_core), + "mantle_type": typeof (value_mantle) + }); } core[key] = value_mantle; // throw (new Error(message)); @@ -9393,23 +9765,26 @@ var lib_plankton; } object_1.patch = patch; /** - * @author fenris + * @deprecated use Object.assign instead! */ - function patched(core, mantle, deep) { - if (deep === void 0) { deep = undefined; } + function patched(core, mantle, options) { + if (options === void 0) { options = {}; } + options = Object.assign({ + "deep": true + }, options); var result = {}; - patch(result, core, deep); - patch(result, mantle, deep); + patch(result, core, { "deep": options.deep }); + patch(result, mantle, { "deep": options.deep }); return result; } object_1.patched = patched; /** - * @author fenris + * @deprecated use Object.assign instead! */ function attached(object, key, value) { var mantle = {}; mantle[key] = value; - return patched(object, mantle, false); + return patched(object, mantle, { "deep": false }); } object_1.attached = attached; /** @@ -9830,6 +10205,339 @@ var lib_plankton; api.class_api = class_api; })(api = lib_plankton.api || (lib_plankton.api = {})); })(lib_plankton || (lib_plankton = {})); +"use strict"; +/* +This file is part of »bacterio-plankton:http_base«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:http_base« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:http_base« 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 +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:http_base«. If not, see . + */ +/* +This file is part of »bacterio-plankton:http_base«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:http_base« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:http_base« 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 +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:http_base«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var http_base; + (function (http_base) { + /** + */ + const linebreak = "\r\n"; + /** + */ + function capitalize_all(str) { + return str.split("-").map(x => lib_plankton.string.capitalize(x)).join("-"); + } + /** + */ + function encode_request(encode_method, request) { + let request_raw = ""; + request_raw += lib_plankton.string.coin("{{method}} {{path}}{{query}} {{version}}{{linebreak}}", { + "method": encode_method(request.method), + "path": request.path, + "query": ((request.query === null) ? "" : request.query), + "version": request.version, + "linebreak": linebreak, + }); + if (request.host === null) { + // do nothing + } + else { + request_raw += lib_plankton.string.coin("Host: {{host}}{{linebreak}}", { + "host": request.host, + "linebreak": linebreak, + }); + } + for (const [key, value] of Object.entries(request.headers)) { + request_raw += lib_plankton.string.coin("{{key}}: {{value}}{{linebreak}}", { + "key": capitalize_all(key), + "value": value, + "linebreak": linebreak, + }); + } + request_raw += linebreak; + if (request.body === null) { + // do nothing + } + else { + request_raw += request.body.toString(); + } + return request_raw; + } + http_base.encode_request = encode_request; + /** + */ + function decode_request(decode_method, has_body, request_raw) { + const lines = lib_plankton.string.split(request_raw, linebreak); + if (lines.length <= 0) { + throw (new Error("malformed request")); + } + else { + const first = lines[0]; + lines.shift(); + const parts = first.split(" "); + const method = decode_method(parts[0]); + const path_and_query = parts[1]; + const parts_ = path_and_query.split("?"); + const path = parts_[0]; + const query = ((parts_.length <= 1) ? null : ("?" + parts_.slice(1).join("?"))); + const version = parts[2]; + let headers = {}; + while (true) { + const line = lines[0]; + lines.shift(); + if (line === "") { + break; + } + else { + const [key, value] = line.split(": ", 2); + headers[key.toLowerCase()] = value; + } + } + const body = (has_body(method) + // @ts-ignore + ? Buffer.from(lines.join(linebreak)) + : null); + const request = { + // TODO + "scheme": "http", + "host": (headers["host"] ?? null), + "path": path, + "version": version, + "method": method, + "query": query, + "headers": headers, + "body": body, + }; + return request; + } + } + http_base.decode_request = decode_request; + /** + */ + function encode_response(encode_status_code, get_status_text, response) { + let response_raw = ""; + response_raw += lib_plankton.string.coin("{{version}} {{status_code}} {{status_text}}{{linebreak}}", { + "version": (response.version ?? ""), + "status_code": encode_status_code(response.status_code), + "status_text": get_status_text(response.status_code), + "linebreak": linebreak, + }); + for (const [key, value] of Object.entries(response.headers)) { + response_raw += lib_plankton.string.coin("{{key}}: {{value}}{{linebreak}}", { + "key": capitalize_all(key), + "value": value, + "linebreak": linebreak, + }); + } + response_raw += linebreak; + if (response.body === null) { + // do nothing + } + else { + response_raw += response.body.toString(); + } + return response_raw; + } + http_base.encode_response = encode_response; + /** + */ + function decode_response(decode_status_code, response_raw) { + const lines = response_raw.split(linebreak); + const first = lines[0]; + lines.shift(); + const first_parts = first.split(" "); + const version = first_parts[0]; + const status_code = decode_status_code(first_parts[1]); + // first_parts.slice(2) ? probably irrelevant + let headers = {}; + while (true) { + const line = lines[0]; + lines.shift(); + if (line === "") { + break; + } + else { + const [key, value] = line.split(": ", 2); + headers[key.toLowerCase()] = value; + } + } + // @ts-ignore + const body = Buffer.from(lines.join(linebreak)); + const response = { + // TODO + "version": version, + "status_code": status_code, + "headers": headers, + "body": body, + }; + return response; + } + http_base.decode_response = decode_response; + /** + * executes an HTTP request + * + * @todo define type_signal + */ + async function call(has_body, encode_method, decode_status_code, request, { "timeout": option_timeout = 5.0, "follow_redirects": option_follow_redirects = false, "implementation": option_implementation = "fetch", } = {}) { + const target = lib_plankton.string.coin("{{scheme}}://{{host}}{{path}}{{query}}", { + "scheme": request.scheme, + "host": (request.host ?? ""), + "path": request.path, + "query": (request.query ?? ""), + }); + switch (option_implementation) { + default: { + return Promise.reject(new Error("invalid implementation: " + option_implementation)); + break; + } + case "fetch": { + function core(signal) { + return (fetch(target, Object.assign({ + "method": encode_method(request.method), + "headers": request.headers, + /* + "redirect": ( + option_follow_redirects + ? + "follow" + : + "manual" + ), + */ + "signal": (signal + ?? + undefined), + // "keepalive": false, + }, ((has_body(request.method) + && + (request.body !== null)) + ? { + "body": request.body.toString(), + } + : {}))) + .catch((reason) => { + // console.info(reason); + return Promise.reject(reason); + }) + .then((response_raw) => (response_raw.text() + .then((body) => Promise.resolve({ + // TODO + "version": null, + "status_code": decode_status_code(response_raw.status.toFixed(0)), + "headers": ((headers_raw => { + let headers = {}; + headers_raw.forEach((value, key) => { + headers[key] = value; + }); + return headers; + })(response_raw.headers)), + "body": body, + }))))); + } + function timeout(controller) { + return (new Promise((resolve, reject) => { + if (option_timeout === null) { + // do nothing (neither resolve nor reject ever) + } + else { + setTimeout(() => { + controller.abort(); + resolve(null); + }, (option_timeout * 1000)); + } + })); + } + const controller = new AbortController(); + const signal = controller.signal; + const response = await Promise.race([ + timeout(controller), + core(signal), + ]); + if (response === null) { + throw (new Error("http_request_timeout")); + } + else { + return response; + } + break; + } + case "http_module": { + // @ts-ignore + const nm_http = require("http"); + // @ts-ignore + const nm_https = require("https"); + return (new Promise((resolve, reject) => { + const req = ((request.scheme === "https") + ? nm_https + : nm_http) + .request(target, { + "method": request.method, + "headers": request.headers, + }, + // @ts-ignore + (res) => { + try { + let response_body = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + response_body += chunk; + }); + res.on("end", () => { + resolve({ + // TODO + "version": null, + "status_code": res.statusCode, + "headers": res.headers, + "body": response_body, + }); + }); + } + catch (error) { + reject(error); + } + }); + req.on("error", (error) => { + reject(error); + }); + req.write(request.body); + req.end(); + })); + break; + } + } + } + http_base.call = call; + })(http_base = lib_plankton.http_base || (lib_plankton.http_base = {})); +})(lib_plankton || (lib_plankton = {})); /* This file is part of »bacterio-plankton:http«. @@ -9866,6 +10574,65 @@ var lib_plankton; enum_method["put"] = "put"; enum_method["patch"] = "patch"; })(enum_method = http.enum_method || (http.enum_method = {})); + /** + */ + let enum_status_code; + (function (enum_status_code) { + enum_status_code[enum_status_code["continue_"] = 100] = "continue_"; + enum_status_code[enum_status_code["switching_protocols"] = 101] = "switching_protocols"; + enum_status_code[enum_status_code["early_hints"] = 103] = "early_hints"; + enum_status_code[enum_status_code["ok"] = 200] = "ok"; + enum_status_code[enum_status_code["created"] = 201] = "created"; + enum_status_code[enum_status_code["accepted"] = 202] = "accepted"; + enum_status_code[enum_status_code["non_authoritative_information"] = 203] = "non_authoritative_information"; + enum_status_code[enum_status_code["no_content"] = 204] = "no_content"; + enum_status_code[enum_status_code["reset_content"] = 205] = "reset_content"; + enum_status_code[enum_status_code["partial_coentent"] = 206] = "partial_coentent"; + enum_status_code[enum_status_code["multiple_choices"] = 300] = "multiple_choices"; + enum_status_code[enum_status_code["moved_permanently"] = 301] = "moved_permanently"; + enum_status_code[enum_status_code["found"] = 302] = "found"; + enum_status_code[enum_status_code["see_other"] = 303] = "see_other"; + enum_status_code[enum_status_code["not_modified"] = 304] = "not_modified"; + enum_status_code[enum_status_code["temporary_redirect"] = 307] = "temporary_redirect"; + enum_status_code[enum_status_code["permanent_redirect"] = 308] = "permanent_redirect"; + enum_status_code[enum_status_code["bad_request"] = 400] = "bad_request"; + enum_status_code[enum_status_code["unauthorized"] = 401] = "unauthorized"; + enum_status_code[enum_status_code["payment_required"] = 402] = "payment_required"; + enum_status_code[enum_status_code["forbidden"] = 403] = "forbidden"; + enum_status_code[enum_status_code["not_found"] = 404] = "not_found"; + enum_status_code[enum_status_code["method_not_allowed"] = 405] = "method_not_allowed"; + enum_status_code[enum_status_code["not_acceptable"] = 406] = "not_acceptable"; + enum_status_code[enum_status_code["proxy_authentication_required"] = 407] = "proxy_authentication_required"; + enum_status_code[enum_status_code["request_timeout"] = 408] = "request_timeout"; + enum_status_code[enum_status_code["conflict"] = 409] = "conflict"; + enum_status_code[enum_status_code["gone"] = 410] = "gone"; + enum_status_code[enum_status_code["length_required"] = 411] = "length_required"; + enum_status_code[enum_status_code["precondition_failed"] = 412] = "precondition_failed"; + enum_status_code[enum_status_code["payload_too_large"] = 413] = "payload_too_large"; + enum_status_code[enum_status_code["uri_too_long"] = 414] = "uri_too_long"; + enum_status_code[enum_status_code["unsupported_media_type"] = 415] = "unsupported_media_type"; + enum_status_code[enum_status_code["range_not_satisfiable"] = 416] = "range_not_satisfiable"; + enum_status_code[enum_status_code["expectation_failed"] = 417] = "expectation_failed"; + enum_status_code[enum_status_code["i_m_a_teapot"] = 418] = "i_m_a_teapot"; + enum_status_code[enum_status_code["unprocessable_entity"] = 422] = "unprocessable_entity"; + enum_status_code[enum_status_code["too_early"] = 425] = "too_early"; + enum_status_code[enum_status_code["upgrade_required"] = 426] = "upgrade_required"; + enum_status_code[enum_status_code["precondition_required"] = 428] = "precondition_required"; + enum_status_code[enum_status_code["too_many_requests"] = 429] = "too_many_requests"; + enum_status_code[enum_status_code["request_header_fields_too_large"] = 431] = "request_header_fields_too_large"; + enum_status_code[enum_status_code["unavailable_for_legal_reasons"] = 451] = "unavailable_for_legal_reasons"; + enum_status_code[enum_status_code["internal_server_error"] = 500] = "internal_server_error"; + enum_status_code[enum_status_code["not_implemented"] = 501] = "not_implemented"; + enum_status_code[enum_status_code["bad_gateway"] = 502] = "bad_gateway"; + enum_status_code[enum_status_code["service_unavailable"] = 503] = "service_unavailable"; + enum_status_code[enum_status_code["gateway_timeout"] = 504] = "gateway_timeout"; + enum_status_code[enum_status_code["http_version_not_supported"] = 505] = "http_version_not_supported"; + enum_status_code[enum_status_code["variant_also_negotiates"] = 506] = "variant_also_negotiates"; + enum_status_code[enum_status_code["insufficient_storage"] = 507] = "insufficient_storage"; + enum_status_code[enum_status_code["loop_detected"] = 508] = "loop_detected"; + enum_status_code[enum_status_code["not_extended"] = 510] = "not_extended"; + enum_status_code[enum_status_code["network_authentication"] = 511] = "network_authentication"; + })(enum_status_code = http.enum_status_code || (http.enum_status_code = {})); })(http = lib_plankton.http || (lib_plankton.http = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -9891,22 +10658,6 @@ var lib_plankton; (function (lib_plankton) { var http; (function (http) { - /** - * @author fenris - */ - const linebreak = "\r\n"; - /** - * @todo outsource to string module - */ - function capitalize(str) { - return (str[0].toUpperCase() + str.slice(1)); - } - /** - * @todo outsource to string module - */ - function capitalize_all(str) { - return str.split("-").map(x => capitalize(x)).join("-"); - } /** * @author fenris */ @@ -9938,10 +10689,22 @@ var lib_plankton; default: throw (new Error("unhandled method: " + method_raw)); } } + http.decode_method = decode_method; + /** + */ + function encode_status_code(status_code) { + return status_code.toFixed(0); + } + /** + * @todo check for existance + */ + function decode_status_code(status_code_raw) { + return parseInt(status_code_raw); + } /** * @author fenris */ - function get_statustext(statuscode) { + function get_status_text(statuscode) { switch (statuscode) { case 100: return "Continue"; case 101: return "Switching Protocols"; @@ -10000,140 +10763,43 @@ var lib_plankton; default: throw (new Error("unhandled statuscode: " + statuscode.toFixed(0))); } } + /** + * @author fenris + */ + function has_body(method) { + return [ + http.enum_method.post, + http.enum_method.put, + http.enum_method.patch + ].includes(method); + } + http.has_body = has_body; /** * @author fenris */ function encode_request(request) { - let request_raw = ""; - request_raw += (encode_method(request.method) - + - " " - + - request.path - + - ((request.query === null) ? "" : request.query) - + - " " - + - request.version - + - linebreak); - if (request.host === null) { - // do nothing - } - else { - request_raw += ("Host: " + request.host + linebreak); - } - for (const [key, value] of Object.entries(request.headers)) { - request_raw += (capitalize_all(key) + ": " + value + linebreak); - } - request_raw += linebreak; - if (request.body === null) { - // do nothing - } - else { - request_raw += request.body.toString(); - } - return request_raw; + return lib_plankton.http_base.encode_request(encode_method, request); } http.encode_request = encode_request; /** * @author fenris */ function decode_request(request_raw) { - const lines = request_raw.split(linebreak); - const first = lines.shift(); - const parts = first.split(" "); - const method = decode_method(parts[0]); - const path_and_query = parts[1]; - const parts_ = path_and_query.split("?"); - const path = parts_[0]; - const query = ((parts_.length <= 1) ? null : ("?" + parts_.slice(1).join("?"))); - const version = parts[2]; - let headers = {}; - while (true) { - const line = lines.shift(); - if (line === "") { - break; - } - else { - const [key, value] = line.split(": ", 2); - headers[key.toLowerCase()] = value; - } - } - const body = ([http.enum_method.post, http.enum_method.put, http.enum_method.patch].includes(method) - // @ts-ignore - ? Buffer.from(lines.join(linebreak)) - : null); - const request = { - // TODO - "scheme": "http", - "host": (headers["host"] ?? null), - "path": path, - "version": version, - "method": method, - "query": query, - "headers": headers, - "body": body, - }; - return request; + return lib_plankton.http_base.decode_request(decode_method, has_body, request_raw); } http.decode_request = decode_request; /** * @author fenris */ function encode_response(response) { - let response_raw = ""; - response_raw += (response.version - + - " " - + - response.status_code.toFixed(0) - + - " " - + - get_statustext(response.status_code) - + - linebreak); - for (const [key, value] of Object.entries(response.headers)) { - response_raw += (capitalize_all(key) + ": " + value + linebreak); - } - response_raw += linebreak; - response_raw += response.body; - return response_raw; + return lib_plankton.http_base.encode_response(encode_status_code, get_status_text, response); } http.encode_response = encode_response; /** * @author fenris */ function decode_response(response_raw) { - const lines = response_raw.split(linebreak); - const first = lines.shift(); - const first_parts = first.split(" "); - const version = first_parts[0]; - const status_code = parseInt(first_parts[1]); - // first_parts.slice(2) ? probably irrelevant - let headers = {}; - while (true) { - const line = lines.shift(); - if (line === "") { - break; - } - else { - const [key, value] = line.split(": ", 2); - headers[key.toLowerCase()] = value; - } - } - // @ts-ignore - const body = Buffer.from(lines.join(linebreak)); - const response = { - // TODO - "version": version, - "status_code": status_code, - "headers": headers, - "body": body, - }; - return response; + return lib_plankton.http_base.decode_response(decode_status_code, response_raw); } http.decode_response = decode_response; /** @@ -10141,296 +10807,59 @@ var lib_plankton; * * @todo define type_signal */ - async function call(request, options = {}) { - options = Object.assign({ - "timeout": 5.0, - "follow_redirects": false, - "implementation": "fetch", - }, options); - const target = (request.scheme - + - "://" - + - request.host - + - request.path - + - ((request.query === null) - ? "" - : request.query)); - switch (options.implementation) { - default: { - return Promise.reject("invalid implementation: " + options.implementation); - break; - } - case "fetch": { - function core(signal) { - return (fetch(target, Object.assign({ - "method": ((method => { - switch (method) { - case http.enum_method.head: return "HEAD"; - case http.enum_method.options: return "OPTIONS"; - case http.enum_method.get: return "GET"; - case http.enum_method.delete: return "DELETE"; - case http.enum_method.post: return "POST"; - case http.enum_method.put: return "PUT"; - case http.enum_method.patch: return "PATCH"; - } - })(request.method)), - "headers": request.headers, - /* - "redirect": ( - options.follow_redirects - ? - "follow" - : - "manual" - ), - */ - "signal": (signal - ?? - undefined), - // "keepalive": false, - }, ((((method => { - switch (method) { - case http.enum_method.head: return false; - case http.enum_method.options: return false; - case http.enum_method.get: return false; - case http.enum_method.delete: return false; - case http.enum_method.post: return true; - case http.enum_method.put: return true; - case http.enum_method.patch: return true; - } - })(request.method)) - && - (request.body !== null)) - ? { - "body": request.body.toString(), - } - : {}))) - .catch((reason) => { - // console.info(reason); - return Promise.reject(reason); - }) - .then((response_raw) => (response_raw.text() - .then((body) => Promise.resolve({ - // TODO - "version": null, - "status_code": response_raw.status, - "headers": ((headers_raw => { - let headers = {}; - headers_raw.forEach((value, key) => { - headers[key] = value; - }); - return headers; - })(response_raw.headers)), - "body": body, - }))))); - } - function timeout(controller) { - return (new Promise((resolve, reject) => { - if (options.timeout === null) { - // do nothing (neither resolve nor reject ever) - } - else { - setTimeout(() => { - controller.abort(); - resolve(null); - }, (options.timeout * 1000)); - } - })); - } - const controller = new AbortController(); - const signal = controller.signal; - const response = await Promise.race([ - timeout(controller), - core(signal), - ]); - if (response === null) { - throw (new Error("http_request_timeout")); - } - else { - return response; - } - break; - } - case "http_module": { - // @ts-ignore - const nm_http = require("http"); - // @ts-ignore - const nm_https = require("https"); - return (new Promise((resolve, reject) => { - const req = ((request.scheme === "https") - ? nm_https - : nm_http) - .request(target, { - "method": request.method, - "headers": request.headers, - }, (res) => { - try { - let response_body = ""; - res.setEncoding("utf8"); - res.on("data", (chunk) => { - response_body += chunk; - }); - res.on("end", () => { - resolve({ - // TODO - "version": null, - "status_code": res.statusCode, - "headers": res.headers, - "body": response_body, - }); - }); - } - catch (error) { - reject(error); - } - }); - req.on("error", (error) => { - reject(error); - }); - req.write(request.body); - req.end(); - })); - break; - } - } + async function call(request, { "timeout": option_timeout = 5.0, "follow_redirects": option_follow_redirects = false, "implementation": option_implementation = "fetch", } = {}) { + return lib_plankton.http_base.call(has_body, encode_method, decode_status_code, request, { + "timeout": option_timeout, + "follow_redirects": option_follow_redirects, + "implementation": option_implementation, + }); } http.call = call; })(http = lib_plankton.http || (lib_plankton.http = {})); })(lib_plankton || (lib_plankton = {})); +"use strict"; /* -This file is part of »bacterio-plankton:http«. +This file is part of »bacterio-plankton:rest_base«. Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' -»bacterio-plankton:http« is free software: you can redistribute it and/or modify +»bacterio-plankton:rest_base« is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. -»bacterio-plankton:http« is distributed in the hope that it will be useful, +»bacterio-plankton:rest_base« 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 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:http«. If not, see . +along with »bacterio-plankton:rest_base«. If not, see . + */ +/* +This file is part of »bacterio-plankton:rest_base«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:rest_base« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:rest_base« 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 +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:rest_base«. If not, see . */ var lib_plankton; (function (lib_plankton) { - var http; - (function (http) { - /** - * @author fenris - */ - class class_http_request { - /** - * @author fenris - */ - constructor() { - } - /** - * @implementation - * @author fenris - */ - encode(x) { - return http.encode_request(x); - } - /** - * @implementation - * @author fenris - */ - decode(x) { - return http.decode_request(x); - } - } - http.class_http_request = class_http_request; - /** - * @author fenris - */ - class class_http_response { - /** - * @author fenris - */ - constructor() { - } - /** - * @implementation - * @author fenris - */ - encode(x) { - return http.encode_response(x); - } - /** - * @implementation - * @author fenris - */ - decode(x) { - return http.decode_response(x); - } - } - http.class_http_response = class_http_response; - })(http = lib_plankton.http || (lib_plankton.http = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:rest«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:rest« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:rest« 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 -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:rest«. If not, see . - */ -/* -This file is part of »bacterio-plankton:rest«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:rest« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:rest« 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 -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:rest«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var rest; - (function (rest_1) { - /** - */ - function http_request_method_to_oas(http_request_method) { - switch (http_request_method) { - case lib_plankton.http.enum_method.get: return "get"; - case lib_plankton.http.enum_method.post: return "post"; - case lib_plankton.http.enum_method.patch: return "patch"; - case lib_plankton.http.enum_method.head: return "head"; - case lib_plankton.http.enum_method.delete: return "delete"; - case lib_plankton.http.enum_method.options: return "options"; - case lib_plankton.http.enum_method.put: return "put"; - default: throw (new Error("impossible")); - } - } + var rest_base; + (function (rest_base) { /** */ function wildcard_step_decode(step) { @@ -10469,11 +10898,11 @@ var lib_plankton; } /** */ - function routenode_spawn(steps, http_method, operation) { + function routenode_spawn(encode_http_method, steps, http_method, operation) { let routenode; if (steps.length <= 0) { routenode = { - "operations": Object.fromEntries([[http_method, operation]]), + "operations": Object.fromEntries([[encode_http_method(http_method).toLowerCase(), operation]]), "sub_branch": {}, "sub_wildcard": null, }; @@ -10481,7 +10910,7 @@ var lib_plankton; else { const steps_head = steps[0]; const steps_tail = steps.slice(1); - const sub = routenode_spawn(steps_tail, http_method, operation); + const sub = routenode_spawn(encode_http_method, steps_tail, http_method, operation); const wildcard_name = wildcard_step_decode(steps_head); if (wildcard_name === null) { // branch @@ -10532,7 +10961,7 @@ var lib_plankton; // do nothing } else { - lib_plankton.log.warning("rest_overwriting_path_parameter", { + lib_plankton.log.warning("plankton.rest_base.overwriting_path_parameter", { "key": routenode.sub_wildcard.name, "value_old": result.parameters[routenode.sub_wildcard.name], "value_new": path_head, @@ -10558,21 +10987,22 @@ var lib_plankton; } /** */ - function routenode_path_write(routenode, steps, http_method, operation, options = {}) { + function routenode_path_write(encode_http_method, routenode, steps, http_method, operation, options = {}) { options = lib_plankton.object.patched({ "create": false, }, options); + const http_method_encoded = encode_http_method(http_method).toLowerCase(); if (steps.length <= 0) { - if (!(http_method in routenode.operations)) { + if (!(http_method_encoded in routenode.operations)) { // do nothing } else { - lib_plankton.log.warning("rest_overwriting_action", { + lib_plankton.log.warning("plankton.rest_base.overwriting_action", { "http_method": http_method, "steps": steps, }); } - routenode.operations[http_method] = operation; + routenode.operations[http_method_encoded] = operation; } else { const steps_head = steps[0]; @@ -10587,7 +11017,7 @@ var lib_plankton; else { routenode.sub_wildcard = { "name": wildcard_name, - "node": routenode_spawn(steps_tail, http_method, operation), + "node": routenode_spawn(encode_http_method, steps_tail, http_method, operation), }; } } @@ -10595,7 +11025,7 @@ var lib_plankton; if (!(routenode.sub_wildcard.name === wildcard_name)) { /* lib_plankton.log.warning( - "rest_overwriting_wildcard_node", + "plankton.rest_base.overwriting_wildcard_node", { "wildcard_name": wildcard_name, } @@ -10605,14 +11035,14 @@ var lib_plankton; } else { // walk - routenode_path_write(routenode.sub_wildcard.node, steps_tail, http_method, operation, options); + routenode_path_write(encode_http_method, routenode.sub_wildcard.node, steps_tail, http_method, operation, options); } } } else { if (steps_head in routenode.sub_branch) { // walk branch - routenode_path_write(routenode.sub_branch[steps_head], steps_tail, http_method, operation, options); + routenode_path_write(encode_http_method, routenode.sub_branch[steps_head], steps_tail, http_method, operation, options); } else { // add branch @@ -10620,7 +11050,7 @@ var lib_plankton; throw (new Error("may not create missing route")); } else { - routenode.sub_branch[steps_head] = routenode_spawn(steps_tail, http_method, operation); + routenode.sub_branch[steps_head] = routenode_spawn(encode_http_method, steps_tail, http_method, operation); } } } @@ -10628,141 +11058,130 @@ var lib_plankton; } /** */ - function make(options = {}) { - options = lib_plankton.object.patched({ - "title": "REST-API", - "versioning_method": "none", - "versioning_header_name": "X-Api-Version", - "versioning_query_key": "version", - "header_parameters": [], - "set_access_control_headers": false, - "authentication": { - "kind": "none", - "parameters": {}, - }, - "actions": [], - }, options); + function make(encode_http_method, { "title": option_title = "REST-API", "versioning_method": option_versioning_method = "none", "versioning_header_name": option_versioning_header_name = "X-Api-Version", "versioning_query_key": option_versioning_query_key = "version", "header_parameters": option_header_parameters = [], "set_access_control_headers": option_set_access_control_headers = false, "authentication": option_authentication = { + "kind": "none", + "data": {}, + }, "actions": option_actions = [], } = {}) { const subject = { - "api": lib_plankton.api.make(options.title), - "versioning_method": options.versioning_method, - "versioning_header_name": options.versioning_header_name, - "versioning_query_key": options.versioning_query_key, + "api": lib_plankton.api.make(option_title ?? ""), + "versioning_method": option_versioning_method, + "versioning_header_name": option_versioning_header_name, + "versioning_query_key": option_versioning_query_key, "routetree": { "operations": {}, "sub_branch": {}, "sub_wildcard": null, }, - "header_parameters": options.header_parameters, - "set_access_control_headers": options.set_access_control_headers, - "authentication": options.authentication, + "header_parameters": option_header_parameters, + "set_access_control_headers": option_set_access_control_headers, + "authentication": option_authentication, }; - options.actions.forEach(action_definition => { - rest.register(subject, action_definition.http_method, action_definition.path, action_definition.options); + option_actions.forEach((action_definition) => { + register(encode_http_method, subject, action_definition.http_method, action_definition.path, action_definition.options); }); return subject; } - rest_1.make = make; + rest_base.make = make; /** */ - function register(rest, http_method, path, options) { - options = lib_plankton.object.patched({ - "active": ((version) => true), - "execution": ((stuff) => Promise.resolve({ "status_code": 501, "data": null })), - "restriction": ((stuff) => Promise.resolve(true)), - "input_schema": ((version) => ({})), - "output_schema": ((version) => ({})), - "title": null, - "description": null, - "query_parameters": [], - "request_body_mimetype": "application/json", - "request_body_decode": ((http_request_body, http_request_header_content_type) => (((http_request_header_content_type !== null) - && - (http_request_header_content_type.startsWith("application/json")) - && - (http_request_body !== null) - && - (http_request_body.toString() !== "")) - ? JSON.parse(http_request_body.toString()) - : ((http_request_body !== null) - ? http_request_body.toString() - : null))), - "response_body_mimetype": "application/json", - // TODO: no "from"? - "response_body_encode": ((output) => Buffer["from"](JSON.stringify(output))), - }, options); + function register(encode_http_method, rest, http_method, path, { "active": option_active = (version) => true, "execution": option_execution = (version) => (stuff) => Promise.resolve({ "status_code": 501, "data": null }), "restriction": option_restriction = (version) => (stuff) => Promise.resolve(true), "title": option_title = (version) => null, "description": option_description = (version) => null, "query_parameters": option_query_parameters = (version) => ([]), "input_schema": option_input_schema = (version) => ({}), "output_schema": option_output_schema = (version) => ({}), "request_body_mimetype": option_request_body_mimetype = (version) => "application/json", "request_body_decode": option_request_body_decode = (version) => (http_request_body, http_request_header_content_type) => Promise.resolve(((http_request_header_content_type !== null) + && + (http_request_header_content_type.startsWith("application/json")) + && + (http_request_body !== null) + && + (http_request_body.toString() !== "")) + ? + JSON.parse(http_request_body.toString()) + : + ((http_request_body !== null) + ? + http_request_body.toString() + : + null)), "response_body_mimetype": option_response_body_mimetype = (version) => "application/json", + /** + * @todo no "from"? + */ + "response_body_encode": option_response_body_encode = (version) => (output) => Promise.resolve(Buffer["from"](JSON.stringify(output))), } = {}) { const steps = lib_plankton.string.split(path, "/").slice(1); const steps_enriched = ((rest.versioning_method === "path") - ? ["{version}"].concat(steps) - : steps); - const action_name = (steps.concat([lib_plankton.http.encode_method(http_method).toLowerCase()]) + ? + ["{version}"].concat(steps) + : + steps); + const http_method_encoded = encode_http_method(http_method).toLowerCase(); + const action_name = (steps.concat([http_method_encoded]) .join("_")); const operation = { "action_name": action_name, - "query_parameters": options.query_parameters, - "request_body_mimetype": options.request_body_mimetype, - "request_body_decode": options.request_body_decode, - "response_body_mimetype": options.response_body_mimetype, - "response_body_encode": options.response_body_encode, - "input_schema": options.input_schema, - "output_schema": options.output_schema, + "query_parameters": option_query_parameters, + "request_body_mimetype": option_request_body_mimetype, + "request_body_decode": option_request_body_decode, + "response_body_mimetype": option_response_body_mimetype, + "response_body_encode": option_response_body_encode, + "input_schema": option_input_schema, + "output_schema": option_output_schema, }; - routenode_path_write(rest.routetree, steps_enriched, http_method, operation, { + routenode_path_write(encode_http_method, rest.routetree, steps_enriched, http_method, operation, { "create": true, }); lib_plankton.api.register(rest.api, action_name, { - "active": options.active, - "execution": (version, environment, input) => options.execution({ - "version": version, + "active": option_active, + "execution": (version, environment, input) => option_execution(version)({ "path_parameters": environment.path_parameters, "query_parameters": environment.query_parameters, "headers": environment.headers, "input": input }), - "restriction": (version, environment) => options.restriction({ - "version": version, + "restriction": (version, environment) => option_restriction(version)({ "path_parameters": environment.path_parameters, "query_parameters": environment.query_parameters, "headers": environment.headers, }), - "title": options.title, - "description": options.description, + /** + * @todo heed version + */ + "title": option_title(null), + /** + * @todo heed version + */ + "description": option_description(null), // TODO - // "input_shape": options.input_type, - // "output_shape": options.output_type, + // "input_shape": option_input_type, + // "output_shape": option_output_type, }); - lib_plankton.log.debug("rest_route_added", { + lib_plankton.log.debug("plankton.rest_base.route_added", { "http_method": http_method, "path": path, // "routetree": rest.routetree, }); } - rest_1.register = register; + rest_base.register = register; /** * @todo check request body mimetype? * @todo check query paramater validity */ - async function call(rest, http_request, options = {}) { - options = /*lib_plankton.object.patched*/ Object.assign({ - "checklevel_restriction": lib_plankton.api.enum_checklevel.hard, - "checklevel_input": lib_plankton.api.enum_checklevel.soft, - "checklevel_output": lib_plankton.api.enum_checklevel.soft, - }, options); - lib_plankton.log.info("rest_call", { - "http_request": { - "scheme": http_request.scheme, - "host": http_request.host, - "path": http_request.path, - "version": http_request.version, - "method": http_request.method, - "query": http_request.query, - "headers": http_request.headers, - "body": String(http_request.body), - } + async function call(encode_http_method, is_options_request, decode_status_code, rest, http_request, { "checklevel_restriction": option_checklevel_restriction = lib_plankton.api.enum_checklevel.hard, "checklevel_input": option_checklevel_input = lib_plankton.api.enum_checklevel.soft, "checklevel_output": option_checklevel_output = lib_plankton.api.enum_checklevel.soft, "set_content_length": option_set_content_length = false, } = {}) { + lib_plankton.log.info("plankton.rest_base.call_request", { + "scheme": http_request.scheme, + "host": http_request.host, + "path": http_request.path, + "version": http_request.version, + "method": http_request.method, + "query": http_request.query, + "headers": http_request.headers, + "body": ((http_request.body === null) + ? + null + : + lib_plankton.string.limit(http_request.body.toString(), { + "length": 2047, + })), }); // parse target and query parameters // const url_stuff : URL = new URL("http://dummy" + http_request.target); const path = http_request.path; - const query_parameters_raw = new URLSearchParams(http_request.query); + const query_parameters_raw = new URLSearchParams(http_request.query ?? ""); let query_parameters = {}; for (const [key, value] of query_parameters_raw) { query_parameters[key] = value; @@ -10775,7 +11194,7 @@ var lib_plankton; // resolve const stuff = routenode_path_read(rest.routetree, steps); const allowed_methods = (Object.keys(stuff.routenode.operations) - .map(x => lib_plankton.http.encode_method(x)) + .map(x => x.toUpperCase()) .join(", ")); // get version let version; @@ -10790,13 +11209,23 @@ var lib_plankton; break; } case "header": { - version = http_request.headers[rest.versioning_header_name]; - // delete http_request.headers[rest.versioning_header_name]; + if (rest.versioning_header_name === null) { + throw (new Error("versioning_header_name not set")); + } + else { + version = http_request.headers[rest.versioning_header_name]; + // delete http_request.headers[rest.versioning_header_name]; + } break; } case "query": { - version = query_parameters[rest.versioning_query_key]; - // delete query_parameters[rest.versioning_query_key]; + if (rest.versioning_query_key === null) { + throw (new Error("versioning_query_key not set")); + } + else { + version = query_parameters[rest.versioning_query_key]; + // delete query_parameters[rest.versioning_query_key]; + } break; } default: { @@ -10814,81 +11243,97 @@ var lib_plankton; ? [rest.versioning_header_name] : []) .concat((rest.authentication.kind === "key_header") - ? [rest.authentication.parameters["name"]] + ? [rest.authentication.data["name"]] : [])) .join(", ")), "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": allowed_methods, } : {}); + let response; if (stuff.rest.length > 0) { - return { - "version": "HTTP/1.1", - "status_code": 404, + response = { + "version": http_request.version, + "status_code": decode_status_code(404), "headers": {}, "body": null, }; } else { - if (http_request.method === lib_plankton.http.enum_method.options) { - return { - "version": "HTTP/1.1", - "status_code": 200, - "headers": Object.assign({}, additional_response_headers), + /*if (is_options_request(http_request.method)) { + response = { + "version": http_request.version, + "status_code": decode_status_code(option_options_response_status_code), + "headers": Object.assign( + { + }, + additional_response_headers + ), "body": null, }; } - else { - if (!(http_request.method in stuff.routenode.operations)) { - if (Object.keys(stuff.routenode.operations).length <= 0) { - return { - "version": "HTTP/1.1", - "status_code": 404, + else*/ { + const http_method_encoded = encode_http_method(http_request.method).toLowerCase(); + if (!(http_method_encoded in stuff.routenode.operations)) { + // fallback OPTIONS response + if (is_options_request(http_request.method)) { + response = { + "version": http_request.version, + "status_code": decode_status_code(200), "headers": Object.assign({}, additional_response_headers), "body": null, }; } else { - return { - "version": "HTTP/1.1", - "status_code": 405, - "headers": Object.assign({ - "Allow": allowed_methods, - }, additional_response_headers), - "body": null, - }; + if (Object.keys(stuff.routenode.operations).length <= 0) { + return { + "version": http_request.version, + "status_code": decode_status_code(404), + "headers": Object.assign({}, additional_response_headers), + "body": null, + }; + } + else { + return { + "version": http_request.version, + "status_code": decode_status_code(405), + "headers": Object.assign({ + "Allow": allowed_methods, + }, additional_response_headers), + "body": null, + }; + } } } else { // call let result; let error; - const operation = stuff.routenode.operations[http_request.method]; + const operation = stuff.routenode.operations[http_method_encoded]; const stuff_ = { "version": version, "headers": http_request.headers, "path_parameters": stuff.parameters, "query_parameters": query_parameters, "input": ((http_request.body === null) - ? null - : operation.request_body_decode(http_request.body, (http_request.headers["Content-Type"] - ?? - http_request.headers["content-type"] - ?? - null))), + ? + null + : + await operation.request_body_decode(version)(http_request.body, (http_request.headers["Content-Type"] + ?? + http_request.headers["content-type"] + ?? + null))), }; - /* - const allowed : boolean = ( + /*const allowed : boolean = ( (operation.restriction === null) ? true : operation.restriction(stuff_) ); - */ - let response; - /* + if (! allowed) { lib_plankton.log.error( - "rest_access_denied", + "plankton.rest_base.access_denied", { "http_request": { "target": http_request.target, @@ -10896,14 +11341,21 @@ var lib_plankton; "headers": http_request.headers, "body": ( (http_request.body === null) - ? null - : lib_plankton.string.limit(http_request.body.toString(), {"length": 200}) + ? + null + : + lib_plankton.string.limit( + http_request.body.toString(), + { + "length": 2047, + } + ) ), }, } ); response = { - "version": "HTTP/1.1", + "version": http_request.version, "status_code": 403, "headers": Object.assign( { @@ -10914,36 +11366,29 @@ var lib_plankton; }; } else*/ { - try { - result = await lib_plankton.api.call(rest.api, operation.action_name, { - "version": stuff_.version, - "environment": { - "headers": stuff_.headers, - "path_parameters": stuff_.path_parameters, - "query_parameters": stuff_.query_parameters, - }, - "input": stuff_.input, - "checklevel_restriction": options.checklevel_restriction, - "checklevel_input": options.checklevel_input, - "checklevel_output": options.checklevel_output, - }); - error = null; - } - catch (error_) { - result = null; - error = error_; - } + const { "value": result, "error": error } = await lib_plankton.call.try_catch_wrap_async(() => lib_plankton.api.call(rest.api, operation.action_name, { + "version": stuff_.version, + "environment": { + "headers": stuff_.headers, + "path_parameters": stuff_.path_parameters, + "query_parameters": stuff_.query_parameters, + }, + "input": stuff_.input, + "checklevel_restriction": option_checklevel_restriction, + "checklevel_input": option_checklevel_input, + "checklevel_output": option_checklevel_output, + })); if ((result === null) || (error !== null)) { if (error instanceof lib_plankton.api.class_error_permission_denied) { response = { - "version": "HTTP/1.1", - "status_code": 403, + "version": http_request.version, + "status_code": decode_status_code(403), "headers": Object.assign({}, additional_response_headers), "body": null, }; } else { - lib_plankton.log.error("rest_execution_failed", { + lib_plankton.log.error("plankton.rest_base.execution_failed", { "http_request": { "version": http_request.version, "scheme": http_request.scheme, @@ -10952,53 +11397,82 @@ var lib_plankton; "query": http_request.query, "headers": http_request.headers, "body": ((http_request.body === null) - ? null - : lib_plankton.string.limit(http_request.body.toString(), { "length": 200 })), - }, - "error": { - "message": error.toString(), - "file_name": error.fileName, - "line_number": error.lineNumber, - "stack": error.stack, + ? + null + : + lib_plankton.string.limit(http_request.body.toString(), { + "length": 2047 + })), }, + "error": ((error === null) + ? + null + : + { + "message": error.toString(), + "file_name": error.fileName, + "line_number": error.lineNumber, + "stack": error.stack, + }), }); response = { - "version": "HTTP/1.1", - "status_code": 500, + "version": http_request.version, + "status_code": decode_status_code(500), "headers": Object.assign({}, additional_response_headers), - "body": Buffer["from"]("internal error"), + // @ts-ignore + "body": Buffer.from("internal error"), }; } } else { // encode + const body = await operation.response_body_encode(version)(result.data); response = { - "version": "HTTP/1.1", - "status_code": result.status_code, - "headers": Object.assign({ - "Content-Type": operation.response_body_mimetype, - }, additional_response_headers), - "body": operation.response_body_encode(result.data), + "version": http_request.version, + "status_code": decode_status_code(result.status_code), + "headers": Object.assign({}, additional_response_headers, ((body !== null) + ? + { + "Content-Type": operation.response_body_mimetype(version), + } + : + {}), ((option_set_content_length + && + (body !== null)) + ? + // @ts-ignore Buffer HAS a length + { "Content-Length": body.length } + : + {}), (result.extra_headers ?? {})), + "body": body, }; } } - return response; } } } + lib_plankton.log.info("plankton.rest_base.call_response", { + "version": response.version, + "status_code": response.status_code, + "headers": response.headers, + "body": ((response.body === null) + ? + null + : + lib_plankton.string.limit(response.body.toString(), { + "length": 2047, + })), + }); + return response; } } - rest_1.call = call; + rest_base.call = call; /** * @see https://swagger.io/specification/#openrest-object */ - function to_oas(rest, options = {}) { - options = lib_plankton.object.patched({ - "version": null, - "servers": [], - }, options); + function to_oas(http_request_method_to_oas, has_body, rest, { "version": option_version = null, "servers": option_servers = [], } = {}) { const subject = rest; - const version = (options.version ?? "-"); + const version = (option_version ?? "-"); return { "openapi": "3.0.3", "info": { @@ -11006,18 +11480,18 @@ var lib_plankton; "title": (rest.api.title ?? "API"), // "description": (rest.api.description ?? undefined), }, - "servers": options.servers.map(url => ({ "url": url })), + "servers": option_servers.map(url => ({ "url": url })), "components": { - "securitySchemes": (((description) => ({ - "none": {}, - "key_header": { + "securitySchemes": lib_plankton.call.distinguish(rest.authentication, { + "none": ({}) => ({}), + "key_header": ({ "name": name }) => ({ "default_security_schema": { "type": "restKey", "in": "header", - "name": description.parameters["name"], + "name": name, }, - }, - }[description.kind]))(rest.authentication)), + }), + }), }, "security": [ { @@ -11038,24 +11512,31 @@ var lib_plankton; return [ key, lib_plankton.call.convey(entry.node.operations, [ - x => Object.entries(x), + (x) => Object.entries(x), (pairs) => pairs.map(([http_method, operation]) => ([ + /** + * @todo rectify type argument + */ http_request_method_to_oas(http_method), { + /** + * @todo rectify type argument + */ "operationId": (http_request_method_to_oas(http_method) + "_" + path), - "summary": (operation.title + "summary": (operation.action_name ?? [""].concat(steps_).join(" ")), "description": (lib_plankton.api.get_action(rest.api, operation.action_name).description ?? "(missing)"), - "parameters": [].concat( - // header parameters - rest.header_parameters.map(header_parameter => ({ + "parameters": ((new Array()) + // header parameters + .concat(rest.header_parameters + .map(header_parameter => ({ "name": header_parameter.name, "in": "header", "required": header_parameter.required, @@ -11063,12 +11544,12 @@ var lib_plankton; "type": "string", }, "description": (header_parameter.description ?? undefined), - })), - // path parameters - lib_plankton.call.convey(steps_, [ - x => x.map(y => wildcard_step_decode(y)), - x => x.filter(y => (!(y === null))), - x => x.map(y => ({ + }))) + // path parameters + .concat(lib_plankton.call.convey(steps_, [ + (x) => x.map(y => wildcard_step_decode(y)), + (x) => x.filter(y => (!(y === null))), + (x) => x.map(y => ({ "name": y, "in": "path", "required": true, @@ -11076,9 +11557,10 @@ var lib_plankton; "type": "string", }, })), - ]), - // query parameters - operation.query_parameters.map((query_parameter) => ({ + ])) + // query parameters + .concat(operation.query_parameters(option_version) + .map((query_parameter) => ({ "name": query_parameter.name, "in": "query", "required": query_parameter.required, @@ -11086,19 +11568,15 @@ var lib_plankton; "type": "string", }, "description": (query_parameter.description ?? undefined), - }))), - "requestBody": (([ - lib_plankton.http.enum_method.get, - lib_plankton.http.enum_method.head, - lib_plankton.http.enum_method.delete, - ].includes(http_method)) + })))), + "requestBody": ((!has_body(http_method)) ? undefined : { "content": Object.fromEntries([ [ operation.request_body_mimetype, { - "schema": operation.input_schema(options.version), + "schema": operation.input_schema(option_version), } ] ]) @@ -11110,7 +11588,7 @@ var lib_plankton; [ operation.response_body_mimetype, { - "schema": operation.output_schema(options.version), + "schema": operation.output_schema(option_version), } ] ]), @@ -11122,13 +11600,104 @@ var lib_plankton; ]), ]; }), - x => x.filter(y => (Object.keys(y[1]).length > 0)), - x => Object.fromEntries(x), + (x) => x.filter(y => (Object.keys(y[1]).length > 0)), + (x) => Object.fromEntries(x), ]) }; } - rest_1.to_oas = to_oas; - })(rest = lib_plankton.rest || (lib_plankton.rest = {})); + rest_base.to_oas = to_oas; + })(rest_base = lib_plankton.rest_base || (lib_plankton.rest_base = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:rest_http«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:rest_http« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:rest_http« 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 +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:rest_http«. If not, see . + */ +/* +This file is part of »bacterio-plankton:rest_http«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:rest_http« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:rest_http« 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 +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:rest_http«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var rest_http; + (function (rest_http) { + /** + */ + function http_request_method_to_oas(http_request_method) { + switch (http_request_method) { + case lib_plankton.http.enum_method.get: return "get"; + case lib_plankton.http.enum_method.post: return "post"; + case lib_plankton.http.enum_method.patch: return "patch"; + case lib_plankton.http.enum_method.head: return "head"; + case lib_plankton.http.enum_method.delete: return "delete"; + case lib_plankton.http.enum_method.options: return "options"; + case lib_plankton.http.enum_method.put: return "put"; + default: throw (new Error("impossible")); + } + } + /** + */ + function make(options = {}) { + return lib_plankton.rest_base.make(lib_plankton.http.encode_method, options); + } + rest_http.make = make; + /** + */ + function register(rest, http_method, path, options) { + lib_plankton.rest_base.register(lib_plankton.http.encode_method, rest, http_method, path, options); + } + rest_http.register = register; + /** + * @todo check request body mimetype? + * @todo check query paramater validity + * @todo improve status code mapping + */ + async function call(rest, http_request, { "checklevel_restriction": option_checklevel_restriction = lib_plankton.api.enum_checklevel.hard, "checklevel_input": option_checklevel_input = lib_plankton.api.enum_checklevel.soft, "checklevel_output": option_checklevel_output = lib_plankton.api.enum_checklevel.soft, "set_content_length": option_set_content_length = false, } = {}) { + return lib_plankton.rest_base.call(lib_plankton.http.encode_method, (x => (x === lib_plankton.http.enum_method.options)), (x => x), rest, http_request, { + "checklevel_restriction": option_checklevel_restriction, + "checklevel_input": option_checklevel_input, + "checklevel_output": option_checklevel_output, + "set_content_length": option_set_content_length, + }); + } + rest_http.call = call; + /** + * @see https://swagger.io/specification/#openrest-object + */ + function to_oas(rest, options = {}) { + return lib_plankton.rest_base.to_oas(http_request_method_to_oas, lib_plankton.http.has_body, rest, options); + } + rest_http.to_oas = to_oas; + })(rest_http = lib_plankton.rest_http || (lib_plankton.rest_http = {})); })(lib_plankton || (lib_plankton = {})); /* This file is part of »bacterio-plankton:server«. @@ -11154,7 +11723,6 @@ var lib_plankton; var server; (function (server) { /** - * @author fenris */ function make(handle, options = {}) { options = Object.assign({ @@ -11172,7 +11740,6 @@ var lib_plankton; } server.make = make; /** - * @author fenris * @deprecated */ function make_old(port, handle) { @@ -11184,7 +11751,6 @@ var lib_plankton; } server.make_old = make_old; /** - * @author fenris * @see https://nodejs.org/api/net.html#serverlistenport-host-backlog-callback */ function start(subject) { @@ -11205,20 +11771,21 @@ var lib_plankton; "ip_address": socket.remoteAddress, }; */ - lib_plankton.log.debug("server_process_input", { - "input": input, + lib_plankton.log.debug("plankton.server.process_input", { + "input": lib_plankton.base.buffer_show(input, { "break_char": "|" }), }); (subject.handle(input /*, metadata*/) .then((output) => { - lib_plankton.log.debug("server_writing", { - "output": output, + lib_plankton.log.debug("plankton.server.writing", { + "output": lib_plankton.base.buffer_show(output, { "break_char": "|" }), }); socket.write(output); socket.end(); }) .catch((error) => { - lib_plankton.log.warning("server_handle_failed", { + lib_plankton.log.warning("plankton.server.handle_failed", { "error": error.toString(), + "stack_trace": error.stack, }); // socket.write(""); socket.end(); @@ -11232,7 +11799,7 @@ var lib_plankton; // do nothing } else { - lib_plankton.log.debug("server_timeout_cancelling"); + lib_plankton.log.debug("plankton.server.timeout_cancelling"); clearTimeout(timeout_handler); timeout_handler = null; } @@ -11244,21 +11811,25 @@ var lib_plankton; else { if (timeout_handler === null) { timeout_handler = setTimeout(() => { - lib_plankton.log.debug("server_timeout_reached"); + lib_plankton.log.debug("plankton.server.timeout_reached"); timeout_handler = null; process_input(); }, (subject.threshold * 1000)); } else { - lib_plankton.log.warning("server_timeout_already_started"); + lib_plankton.log.warning("plankton.server.timeout_already_started"); // do nothing } } }; - lib_plankton.log.info("server_client connected", {}); + lib_plankton.log.info("plankton.server.client_connected"); socket.on("data", (input_chunk_raw) => { - lib_plankton.log.debug("server_reading_chunk", { - "chunk_raw": input_chunk_raw, + lib_plankton.log.debug("plankton.server.reading_chunk", { + "chunk_raw": ((input_chunk_raw instanceof Buffer) + ? + lib_plankton.base.buffer_show(input_chunk_raw, { "break_char": "|" }) + : + input_chunk_raw), }); timeout_stop(); const input_chunk = ((input_chunk_raw instanceof Buffer) @@ -11272,12 +11843,12 @@ var lib_plankton; }); socket.on("end", () => { if (!ended) { - lib_plankton.log.info("server_client_disconnected", {}); + lib_plankton.log.info("plankton.server.client_disconnected"); ended = true; timeout_stop(); } else { - lib_plankton.log.info("server_socket_already_ended"); + lib_plankton.log.info("plankton.server.socket_already_ended"); // do nothing } }); @@ -11287,7 +11858,7 @@ var lib_plankton; process.stderr.write("net_error: " + String(error) + "\n\n"); }); subject.serverobj.listen(subject.port, subject.host, 511, () => { - lib_plankton.log.info("server_listenting", { + lib_plankton.log.info("plankton.server.listenting", { "host": subject.host, "port": subject.port, }); @@ -11297,11 +11868,10 @@ var lib_plankton; } server.start = start; /** - * @author fenris */ function kill(subject) { subject.serverobj.close(); - lib_plankton.log.info("server_stopped", {}); + lib_plankton.log.info("plankton.server.stopped"); } server.kill = kill; })(server = lib_plankton.server || (lib_plankton.server = {})); @@ -12244,7 +12814,9 @@ var lib_plankton; function feed(package_) { let identifier = package_.meta.identifier; if (identifier in _packages) { - lib_plankton.object.patch(_packages[identifier].tree, package_.tree, true); + lib_plankton.object.patch(_packages[identifier].tree, package_.tree, { + "deep": true, + }); } else { if (translate._verbosity >= 2) { diff --git a/misc/conf.example.json b/misc/conf.example.json index 0d14cc6..ca1a24b 100644 --- a/misc/conf.example.json +++ b/misc/conf.example.json @@ -4,6 +4,21 @@ "verbosity": "info", "verification_secret": null }, + "log": [ + { + "kind": "stdout", + "data": { + "threshold": "info" + } + }, + { + "kind": "file", + "data": { + "threshold": "notice", + "path": "/tmp/espe/log.jsonl" + } + } + ], "server": { "port": 4916, "path_base": "" @@ -56,7 +71,12 @@ "login_url": null } }, - "output": { - "authelia": "/tmp/authelia-users.yml" - } + "outputs": [ + { + "kind": "authelia_file", + "data": { + "path": "/tmp/authelia-users.yml" + } + } + ] } diff --git a/source/api/actions/member_delete.ts b/source/api/actions/member_delete.ts index d261f06..40d5b3d 100644 --- a/source/api/actions/member_delete.ts +++ b/source/api/actions/member_delete.ts @@ -19,17 +19,20 @@ namespace _espe.api /** */ export function register_member_delete( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - register( + lib_plankton.rest_http.register< + null, + null + >( rest_subject, lib_plankton.http.enum_method.delete, - "/member/delete/:id", + _espe.api.full_path("/member/delete/:id"), { - "description": "löscht ein vorhandenes Mitglied", - "restriction": restriction_logged_in, - "execution": async ({"path_parameters": path_parameters}) => { + "description": () => "löscht ein vorhandenes Mitglied", + "restriction": () => restriction_logged_in, + "execution": () => async ({"path_parameters": path_parameters}) => { const member_id : _espe.type.member_id = parseInt(path_parameters["id"]); await _espe.service.member.remove(member_id); return Promise.resolve({ diff --git a/source/api/actions/member_info.ts b/source/api/actions/member_info.ts index cf8a133..a5fa539 100644 --- a/source/api/actions/member_info.ts +++ b/source/api/actions/member_info.ts @@ -20,10 +20,10 @@ namespace _espe.api * @todo zeitliche Begrenzung? */ export function register_member_info( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - register< + lib_plankton.rest_http.register< int, ( null @@ -39,9 +39,9 @@ namespace _espe.api >( rest_subject, lib_plankton.http.enum_method.get, - "/member/info/:id", + _espe.api.full_path("/member/info/:id"), { - "description": "gibt Angaben über ein Mitglied aus, die für die Registrierung verwendet werden dürfen", + "description": () => "gibt Angaben über ein Mitglied aus, die für die Registrierung verwendet werden dürfen", "input_schema": () => ({ "type": "number", "nullable": false, @@ -80,18 +80,18 @@ namespace _espe.api "email_address_nominal", ] }), - "query_parameters": [ + "query_parameters": () => [ { "name": "key", "required": true, "description": "Zugriffs-Schlüssel", }, ], - "restriction": restriction_verification( + "restriction": () => restriction_verification( stuff => parseInt(stuff.path_parameters["id"]), stuff => stuff.query_parameters["key"] ), - "execution": async ({"path_parameters": path_parameters, "input": input}) => { + "execution": () => async ({"path_parameters": path_parameters, "input": input}) => { const member_id : _espe.type.member_id = parseInt(path_parameters["id"]); const data : ( null diff --git a/source/api/actions/member_list.ts b/source/api/actions/member_list.ts index 171ca2e..420faa5 100644 --- a/source/api/actions/member_list.ts +++ b/source/api/actions/member_list.ts @@ -19,10 +19,10 @@ namespace _espe.api /** */ export function register_member_list( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - register< + lib_plankton.rest_http.register< null, Array< { @@ -37,10 +37,10 @@ namespace _espe.api >( rest_subject, lib_plankton.http.enum_method.get, - "/member/list", + _espe.api.full_path("/member/list"), { - "description": "listet alle Mitglieder auf", - "query_parameters": [ + "description": () => "listet alle Mitglieder auf", + "query_parameters": () => [ { "name": "search_term", "required": false, @@ -88,8 +88,8 @@ namespace _espe.api ], } }), - "restriction": restriction_logged_in, - "execution": ({"query_parameters": query_parameters}) => ( + "restriction": () => restriction_logged_in, + "execution": () => ({"query_parameters": query_parameters}) => ( _espe.service.member.list(query_parameters["search_term"] ?? null) .then( data => Promise.resolve({ diff --git a/source/api/actions/member_modify.ts b/source/api/actions/member_modify.ts index 03c5939..ac63cff 100644 --- a/source/api/actions/member_modify.ts +++ b/source/api/actions/member_modify.ts @@ -19,10 +19,10 @@ namespace _espe.api /** */ export function register_member_modify( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - register< + lib_plankton.rest_http.register< { email_address_private : (null | string); groups ?: Array; @@ -33,9 +33,9 @@ namespace _espe.api >( rest_subject, lib_plankton.http.enum_method.patch, - "/member/modify/:id", + _espe.api.full_path("/member/modify/:id"), { - "description": "ändert die Angaben eines vorhandenen Mitglieds", + "description": () => "ändert die Angaben eines vorhandenen Mitglieds", "input_schema": () => ({ "nullable": false, "type": "object", @@ -71,8 +71,8 @@ namespace _espe.api "output_schema": () => ({ "nullable": true, }), - "restriction": restriction_logged_in, - "execution": async ({"path_parameters": path_parameters, "input": input}) => { + "restriction": () => restriction_logged_in, + "execution": () => async ({"path_parameters": path_parameters, "input": input}) => { if (input === null) { return Promise.reject(new Error("impossible")); } diff --git a/source/api/actions/member_password_change_execute.ts b/source/api/actions/member_password_change_execute.ts index 5d9d86e..3f493ab 100644 --- a/source/api/actions/member_password_change_execute.ts +++ b/source/api/actions/member_password_change_execute.ts @@ -20,10 +20,10 @@ namespace _espe.api * @todo ausgeklügelte Durchsatzratenbegrenzung */ export function register_member_password_change_execute( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - register< + lib_plankton.rest_http.register< { token : string; password_new : string; @@ -37,9 +37,9 @@ namespace _espe.api >( rest_subject, lib_plankton.http.enum_method.patch, - "/member/password_change/execute/:id", + _espe.api.full_path("/member/password_change/execute/:id"), { - "description": "Führt eine Passwort-Änderung für ein Mitglied durch", + "description": () => "Führt eine Passwort-Änderung für ein Mitglied durch", "input_schema": () => ({ "nullable": false, "type": "object", @@ -88,8 +88,8 @@ namespace _espe.api ] } }), - "restriction": restriction_none, - "execution": ({"path_parameters": path_parameters, "input": input}) => { + "restriction": () => restriction_none, + "execution": () => ({"path_parameters": path_parameters, "input": input}) => { if (input === null) { return Promise.reject(new Error("impossible")); } diff --git a/source/api/actions/member_password_change_initialize.ts b/source/api/actions/member_password_change_initialize.ts index b333ba7..4ecb92e 100644 --- a/source/api/actions/member_password_change_initialize.ts +++ b/source/api/actions/member_password_change_initialize.ts @@ -21,10 +21,10 @@ namespace _espe.api * @todo captcha */ export function register_member_password_change_initialize( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - register< + lib_plankton.rest_http.register< { identifier : string; url_template : string; @@ -33,9 +33,9 @@ namespace _espe.api >( rest_subject, lib_plankton.http.enum_method.post, - "/member/password_change/initialize", + _espe.api.full_path("/member/password_change/initialize"), { - "description": "Versucht dem gegebenen Identifikator ein Mitglied zuzuordnen und sendet dem ermittelten Mitglied einen Passwort-Änderungs-Verweis an die hinterlegte private E-Mail-Adresse", + "description": () => "Versucht dem gegebenen Identifikator ein Mitglied zuzuordnen und sendet dem ermittelten Mitglied einen Passwort-Änderungs-Verweis an die hinterlegte private E-Mail-Adresse", "input_schema": () => ({ "nullable": false, "type": "object", @@ -60,8 +60,8 @@ namespace _espe.api "output_schema": () => ({ "nullable": true }), - "restriction": restriction_none, - "execution": async ({"input": input}) => { + "restriction": () => restriction_none, + "execution": () => async ({"input": input}) => { if (input === null) { return Promise.reject(new Error("impossible")); } diff --git a/source/api/actions/member_project.ts b/source/api/actions/member_project.ts index bafdacf..60431ea 100644 --- a/source/api/actions/member_project.ts +++ b/source/api/actions/member_project.ts @@ -19,10 +19,10 @@ namespace _espe.api /** */ export function register_member_project( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - register< + lib_plankton.rest_http.register< { membership_number : (null | string); name_real_value : string; @@ -38,9 +38,9 @@ namespace _espe.api >( rest_subject, lib_plankton.http.enum_method.post, - "/member/project", + _espe.api.full_path("/member/project"), { - "description": "erstellt ein neues Mitglied und gibt die erzeugte ID aus", + "description": () => "erstellt ein neues Mitglied und gibt die erzeugte ID aus", "input_schema": () => ({ "type": "object", "nullable": false, @@ -84,8 +84,8 @@ namespace _espe.api "type": "number", "nullable": false, }), - "restriction": restriction_logged_in, - "execution": async ({"input": input}) => { + "restriction": () => restriction_logged_in, + "execution": () => async ({"input": input}) => { if (input === null) { return Promise.reject(new Error("impossible")); } diff --git a/source/api/actions/member_read.ts b/source/api/actions/member_read.ts index 66e010c..9df80fe 100644 --- a/source/api/actions/member_read.ts +++ b/source/api/actions/member_read.ts @@ -19,10 +19,10 @@ namespace _espe.api /** */ export function register_member_read( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - register< + lib_plankton.rest_http.register< null, { membership_number : (null | string); @@ -44,9 +44,9 @@ namespace _espe.api >( rest_subject, lib_plankton.http.enum_method.get, - "/member/read/:id", + _espe.api.full_path("/member/read/:id"), { - "description": "gibt ein Mitglied anhand seiner ID aus", + "description": () => "gibt ein Mitglied anhand seiner ID aus", "output_schema": () => ({ "nullable": false, "type": "object", @@ -135,8 +135,8 @@ namespace _espe.api "name_login", ] }), - "restriction": restriction_logged_in, - "execution": async ({"path_parameters": path_parameters, "input": input}) => { + "restriction": () => restriction_logged_in, + "execution": () => async ({"path_parameters": path_parameters, "input": input}) => { const member_id : _espe.type.member_id = parseInt(path_parameters["id"]); const member_object : _espe.type.member_object = await _espe.service.member.get(member_id); return Promise.resolve({ diff --git a/source/api/actions/member_register.ts b/source/api/actions/member_register.ts index 7797bb4..a46ecbe 100644 --- a/source/api/actions/member_register.ts +++ b/source/api/actions/member_register.ts @@ -20,10 +20,10 @@ namespace _espe.api * @todo zeitliche Begrenzung? */ export function register_member_register( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - register< + lib_plankton.rest_http.register< { email_use_veiled_address : boolean; email_use_nominal_address : boolean; @@ -40,9 +40,9 @@ namespace _espe.api >( rest_subject, lib_plankton.http.enum_method.post, - "/member/register/:id", + _espe.api.full_path("/member/register/:id"), { - "description": "nimmt zusätzliche Angaben eines Mitglieds entgegen", + "description": () => "nimmt zusätzliche Angaben eines Mitglieds entgegen", "input_schema": () => ({ "type": "object", "nullable": false, @@ -109,18 +109,18 @@ namespace _espe.api ] } }), - "query_parameters": [ + "query_parameters": () => [ { "name": "key", "required": true, "description": "Zugriffs-Schlüssel", }, ], - "restriction": restriction_verification( + "restriction": () => restriction_verification( stuff => parseInt(stuff.path_parameters["id"]), stuff => stuff.query_parameters["key"] ), - "execution": ({"path_parameters": path_parameters, "input": input}) => { + "execution": () => ({"path_parameters": path_parameters, "input": input}) => { if (input === null) { return Promise.reject(new Error("impossible")); } diff --git a/source/api/actions/member_summon.ts b/source/api/actions/member_summon.ts index 0cbf719..20eef9e 100644 --- a/source/api/actions/member_summon.ts +++ b/source/api/actions/member_summon.ts @@ -19,10 +19,10 @@ namespace _espe.api /** */ export function register_member_summon( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - lib_plankton.rest.register< + lib_plankton.rest_http.register< { url_template : string; }, @@ -36,9 +36,9 @@ namespace _espe.api lib_plankton.http.enum_method.post, _espe.conf.get().server.path_base + "/member/summon/:id", { - "description": "sendet an ein Mitglied eine E-Mail mit Aufforderung zur Registrierung", - "restriction": restriction_logged_in, - "execution": async ({"path_parameters": path_parameters, "input": input}) => { + "description": () => "sendet an ein Mitglied eine E-Mail mit Aufforderung zur Registrierung", + "restriction": () => restriction_logged_in, + "execution": () => async ({"path_parameters": path_parameters, "input": input}) => { if (input === null) { return Promise.reject(new Error("impossible")); } diff --git a/source/api/actions/meta_ping.ts b/source/api/actions/meta_ping.ts index 62f7681..3f2a9ed 100644 --- a/source/api/actions/meta_ping.ts +++ b/source/api/actions/meta_ping.ts @@ -19,10 +19,10 @@ namespace _espe.api /** */ export function register_meta_ping( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - lib_plankton.rest.register< + lib_plankton.rest_http.register< null, string > @@ -31,7 +31,7 @@ namespace _espe.api lib_plankton.http.enum_method.get, _espe.conf.get().server.path_base + "/meta/ping", { - "description": "sendet ein 'pong' zurück; gedacht um die Erreichbarkeit des Backends zu prüfen", + "description": () => "sendet ein 'pong' zurück; gedacht um die Erreichbarkeit des Backends zu prüfen", "input_schema": () => ({ "nullable": true, }), @@ -39,8 +39,8 @@ namespace _espe.api "nullable": false, "type": "string", }), - "restriction": restriction_none, - "execution": () => { + "restriction": () => restriction_none, + "execution": () => () => { return Promise.resolve({ "status_code": 200, "data": "pong", diff --git a/source/api/actions/meta_spec.ts b/source/api/actions/meta_spec.ts index 5dba331..09a1539 100644 --- a/source/api/actions/meta_spec.ts +++ b/source/api/actions/meta_spec.ts @@ -19,10 +19,10 @@ namespace _espe.api /** */ export function register_meta_spec( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - lib_plankton.rest.register< + lib_plankton.rest_http.register< null, any > @@ -31,17 +31,17 @@ namespace _espe.api lib_plankton.http.enum_method.get, _espe.conf.get().server.path_base + "/meta/spec", { - "description": "gibt die API-Spezifikation im OpenAPI-Format aus", + "description": () => "gibt die API-Spezifikation im OpenAPI-Format aus", "input_schema": () => ({ "nullable": true, }), "output_schema": () => ({ }), - "restriction": restriction_none, - "execution": () => { + "restriction": () => restriction_none, + "execution": () => () => { return Promise.resolve({ "status_code": 200, - "data": lib_plankton.rest.to_oas(rest_subject), + "data": lib_plankton.rest_http.to_oas(rest_subject), }); }, } diff --git a/source/api/actions/session_begin.ts b/source/api/actions/session_begin.ts index f5c7e2b..040c00c 100644 --- a/source/api/actions/session_begin.ts +++ b/source/api/actions/session_begin.ts @@ -19,10 +19,10 @@ namespace _espe.api /** */ export function register_session_begin( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - lib_plankton.rest.register< + lib_plankton.rest_http.register< { name : string; password : string; @@ -37,7 +37,7 @@ namespace _espe.api lib_plankton.http.enum_method.post, _espe.conf.get().server.path_base + "/session/begin", { - "description": "führt die Anmeldung am System aus um geschützte Aktionen nutzen zu können", + "description": () => "führt die Anmeldung am System aus um geschützte Aktionen nutzen zu können", "input_schema": () => ({ "type": "object", "properties": { @@ -58,8 +58,8 @@ namespace _espe.api "type": "string", "description": "der Sitzungs-Schlüssel, der als Header 'X-Session-Key' gesetzt werden muss um Erlaubnis zur Ausführung geschützter Aktionen zu erhalten", }), - "restriction": restriction_none, - "execution": async ({"input": input}) => { + "restriction": () => restriction_none, + "execution": () => async ({"input": input}) => { if (input === null) { return Promise.reject(new Error("impossible")); } diff --git a/source/api/actions/session_end.ts b/source/api/actions/session_end.ts index 9ab94ed..0f5990f 100644 --- a/source/api/actions/session_end.ts +++ b/source/api/actions/session_end.ts @@ -19,23 +19,26 @@ namespace _espe.api /** */ export function register_session_end( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_http.type_rest ) : void { - register( + lib_plankton.rest_http.register< + null, + null + >( rest_subject, lib_plankton.http.enum_method.delete, "/session/end", { - "description": "beendet eine Sitzung", + "description": () => "beendet eine Sitzung", "input_schema": () => ({ "type": "null", }), "output_schema": () => ({ "type": "null", }), - "restriction": restriction_logged_in, - "execution": async (stuff) => { + "restriction": () => restriction_logged_in, + "execution": () => async (stuff) => { const session : {key : string; value : lib_plankton.session.type_session} = await session_from_stuff(stuff); await lib_plankton.session.end(session.key); return Promise.resolve({ diff --git a/source/api/base.ts b/source/api/base.ts index c06e34b..8b91cea 100644 --- a/source/api/base.ts +++ b/source/api/base.ts @@ -20,7 +20,7 @@ namespace _espe.api * @todo zu plankton auslagern? */ type type_stuff = { - version: (null | string); + // version: (null | string); headers: Record; path_parameters: Record; query_parameters: Record; @@ -43,9 +43,9 @@ namespace _espe.api */ /* export function restriction_disjunction( - left : lib_plankton.rest.type_restriction, - right : lib_plankton.rest.type_restriction - ) : lib_plankton.rest.type_restriction + left : lib_plankton.rest_http.type_restriction, + right : lib_plankton.rest_http.type_restriction + ) : lib_plankton.rest_http.type_restriction { return ( (stuff) => Promise.any( @@ -61,14 +61,14 @@ namespace _espe.api /** */ - export const restriction_none : lib_plankton.rest.type_restriction = ( + export const restriction_none : lib_plankton.rest_http.type_restriction = ( (stuff) => Promise.resolve(true) ); /** */ - export const restriction_logged_in : lib_plankton.rest.type_restriction = ( + export const restriction_logged_in : lib_plankton.rest_http.type_restriction = ( (stuff) => ( session_from_stuff(stuff) .then(() => Promise.resolve(true)) @@ -82,7 +82,7 @@ namespace _espe.api export function restriction_verification( extract_data : ((stuff : type_stuff) => any), extract_verification : ((stuff : type_stuff) => string) - ) : lib_plankton.rest.type_restriction + ) : lib_plankton.rest_http.type_restriction { return ( (stuff) => _espe.helpers.verification_check( @@ -91,18 +91,28 @@ namespace _espe.api ) ); } + + /** + */ + export function full_path( + path : string + ) : string + { + return (_espe.conf.get().server.path_base + path); + } /** */ + /* export function register( - rest_subject : lib_plankton.rest.type_rest, + rest_subject : lib_plankton.rest_http.type_rest, http_method : lib_plankton.http.enum_method, path : string, options : { - active ?: ((version : string) => boolean); - restriction ?: (null | lib_plankton.rest.type_restriction); - execution ?: lib_plankton.rest.type_execution; + active ?: ((version : (null | string)) => boolean); + restriction ?: (null | lib_plankton.rest_http.type_restriction); + execution ?: lib_plankton.rest_http.type_execution; title ?: (null | string); description ?: (null | string); query_parameters ?: Array< @@ -112,8 +122,8 @@ namespace _espe.api required : boolean; } >; - input_schema ?: ((version: (null | string)) => lib_plankton.rest.type_oas_schema); - output_schema ?: ((version: (null | string)) => lib_plankton.rest.type_oas_schema); + input_schema ?: ((version: (null | string)) => lib_plankton.rest_http.type_oas_schema); + output_schema ?: ((version: (null | string)) => lib_plankton.rest_http.type_oas_schema); request_body_mimetype ?: string; request_body_decode ?: ((http_request_body : Buffer, http_request_header_content_type : (null | string)) => any); response_body_mimetype ?: string; @@ -121,17 +131,12 @@ namespace _espe.api } = {} ) : void { - options = Object.assign( - { - }, - options - ); - lib_plankton.rest.register( + lib_plankton.rest_http.register( rest_subject, http_method, (_espe.conf.get().server.path_base + path), options ); } - + */ } diff --git a/source/api/functions.ts b/source/api/functions.ts index 3a34e22..e2d8fd5 100644 --- a/source/api/functions.ts +++ b/source/api/functions.ts @@ -19,9 +19,9 @@ namespace _espe.api /** */ export function make( - ) : lib_plankton.rest.type_rest + ) : lib_plankton.rest_http.type_rest { - const rest_subject : lib_plankton.rest.type_rest = lib_plankton.rest.make( + const rest_subject : lib_plankton.rest_http.type_rest = lib_plankton.rest_http.make( { "title": "espe", "versioning_method": "header", @@ -29,7 +29,7 @@ namespace _espe.api "set_access_control_headers": true, "authentication": { "kind": "key_header", - "parameters": {"name": "X-Session-Key"} + "data": {"name": "X-Session-Key"} }, } ); @@ -59,7 +59,12 @@ namespace _espe.api _espe.api.register_member_password_change_execute(rest_subject); } } - + // invite + { + _espe.api.register_invite_create(rest_subject); + _espe.api.register_invite_examine(rest_subject); + _espe.api.register_invite_accept(rest_subject); + } return rest_subject; } diff --git a/source/conf.ts b/source/conf.ts index 7caf727..b64301a 100644 --- a/source/conf.ts +++ b/source/conf.ts @@ -40,6 +40,41 @@ namespace _espe.conf ); + /** + */ + export type type_log_channel = ( + { + kind : "stdout"; + data : { + threshold : type_log_threshold; + }; + } + | + { + kind : "file"; + data : { + threshold : type_log_threshold; + path : string; + }; + } + | + { + kind : "email"; + data : { + threshold : type_log_threshold; + smtp_credentials : { + host : string; + port : int; + username : string; + password : string; + }; + sender : string; + receivers : Array; + }; + } + ); + + /** */ export type type_conf = { @@ -48,35 +83,7 @@ namespace _espe.conf verification_secret : (null | string); }; log : Array< - { - kind : "stdout"; - data : { - threshold : type_log_threshold; - }; - } - | - { - kind : "file"; - data : { - threshold : type_log_threshold; - path : string; - }; - } - | - { - kind : "email"; - data : { - threshold : type_log_threshold; - smtp_credentials : { - host : string; - port : int; - username : string; - password : string; - }; - sender : string; - receivers : Array; - }; - } + type_log_channel >; server : { host : string; @@ -454,7 +461,8 @@ namespace _espe.conf return (conf_raw["outputs"] ?? []); break; } - case 4: { + case 4: + case 5: { const node_outputs = (conf_raw["outputs"] ?? []); return node_outputs.map( (output_description : {kind : string; data : any;}) => { diff --git a/source/main.ts b/source/main.ts index 363e078..05bafe4 100644 --- a/source/main.ts +++ b/source/main.ts @@ -13,424 +13,520 @@ You should have received a copy of the GNU General Public License along with thi . */ - -/** - */ -async function main( - args_raw : Array -) : Promise +namespace _espe { - // init - lib_plankton.log.conf_push( - [ - lib_plankton.log.channel_make( + + /** + */ + function setup_log_fallback( + ) : void + { + lib_plankton.log.set_main_logger( + [ { - "kind": "stdout", + "kind": "filtered", "data": { - "threshold": "notice", - // "format": "human_readable", + "core": { + "kind": "std", + "data": { + "target": "stdout", + "format": { + "kind": "human_readable", + "data": {} + } + } + }, + "predicate": [ + [ + { + "item": { + "kind": "level", + "data": {"threshold": "info"} + }, + } + ] + ], + } + }, + ] + ); + } + + + /** + */ + function setup_log_from_conf( + ) : void + { + lib_plankton.log.set_main_logger( + _espe.conf.get().log.map( + log_channel => { + switch (log_channel.kind) { + case "stdout": { + return { + "kind": "filtered", + "data": { + "core": { + "kind": "std", + "data": { + "target": "stdout", + "format": {"kind": "human_readable", "data": {}} + } + }, + "predicate": [ + [ + {"item": {"kind": "level", "data": {"threshold": log_channel.data.threshold}}} + ] + ], + } + }; + break; + } + case "file": { + return { + "kind": "filtered", + "data": { + "core": { + "kind": "file", + "data": { + "path": log_channel.data.path, + "format": {"kind": "jsonl", "data": {"structured": false}} + } + }, + "predicate": [ + [ + {"item": {"kind": "level", "data": {"threshold": log_channel.data.threshold}}} + ] + ], + } + }; + break; + } + case "email": { + return { + "kind": "filtered", + "data": { + "core": { + "kind": "email", + "data": log_channel.data + }, + "predicate": [ + [ + {"item": {"kind": "level", "data": {"threshold": log_channel.data.threshold}}} + ] + ], + } + }; + break; + } + default: { + throw (new Error("unhandled log channel")); + break; + } } } - ), - ] - ); - const language_codes : Array = [ - "deu", - "eng", - ]; - await ( - Promise.all( - language_codes - .map( - language_code => ( - lib_plankton.file.read( - lib_plankton.string.coin( - "data/localization/{{language_code}}.loc.json", - { - "language_code": language_code, - } + ) + ); + } + + + /** + */ + export async function main( + args_raw : Array + ) : Promise + { + // init + setup_log_fallback(); + const language_codes : Array = [ + "deu", + "eng", + ]; + await ( + Promise.all( + language_codes + .map( + language_code => ( + lib_plankton.file.read( + lib_plankton.string.coin( + "data/localization/{{language_code}}.loc.json", + { + "language_code": language_code, + } + ) ) - ) - .then( - content => (new Promise( - (resolve, reject) => { - try { - resolve(JSON.parse(content)); + .then( + content => (new Promise( + (resolve, reject) => { + try { + resolve(JSON.parse(content)); + } + catch (error) { + reject(error); + } } - catch (error) { - reject(error); - } - } - )) + )) + ) ) ) ) - ) - .then( - packages => lib_plankton.translate.initialize( - { - "verbosity": 1, - "packages": packages, - "order": language_codes, - "autopromote": false, - } + .then( + packages => lib_plankton.translate.initialize( + { + "verbosity": 1, + "packages": packages, + "order": language_codes, + "autopromote": false, + } + ) ) - ) - ); - - // args - const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler({ - "action": lib_plankton.args.class_argument.positional({ - "index": 0, - "type": lib_plankton.args.enum_type.string, - "mode": lib_plankton.args.enum_mode.replace, - "default": "serve", - "name": "action", - "info": lib_plankton.string.coin( - "{{description}}:\n{{options}}\n\t\t", - { - "description": lib_plankton.translate.get("help.args.action.options.serve"), - "options": ( - [ - { - "name": "serve", - "description": lib_plankton.translate.get("help.args.action.options.serve"), - }, - { - "name": "api-doc", - "description": lib_plankton.translate.get("help.args.action.options.api_doc") - }, - { - "name": "email-test", - "description": lib_plankton.translate.get("help.args.action.options.email_test") - }, - { - "name": "expose-conf", - "description": lib_plankton.translate.get("help.args.action.options.expose_conf") - }, - { - "name": "password-image", - "description": lib_plankton.translate.get("help.args.action.options.password_image") - }, - { - "name": "admin-add", - "description": lib_plankton.translate.get("help.args.action.options.admin_add") - }, - { - "name": "export-authelia", - "description": lib_plankton.translate.get("help.args.action.options.export_authelia") - }, - { - "name": "help", - "description": lib_plankton.translate.get("help.args.action.options.help") - }, - ] - .map( - entry => lib_plankton.string.coin( - "\t\t- {{name}}\n\t\t\t{{description}}\n", + ); + + // args + const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler({ + "action": lib_plankton.args.class_argument.positional({ + "index": 0, + "type": lib_plankton.args.enum_type.string, + "mode": lib_plankton.args.enum_mode.replace, + "default": "serve", + "name": "action", + "info": lib_plankton.string.coin( + "{{description}}:\n{{options}}\n\t\t", + { + "description": lib_plankton.translate.get("help.args.action.options.serve"), + "options": ( + [ { - "name": entry.name, - "description": entry.description, - } + "name": "serve", + "description": lib_plankton.translate.get("help.args.action.options.serve"), + }, + { + "name": "api-doc", + "description": lib_plankton.translate.get("help.args.action.options.api_doc") + }, + { + "name": "email-test", + "description": lib_plankton.translate.get("help.args.action.options.email_test") + }, + { + "name": "expose-conf", + "description": lib_plankton.translate.get("help.args.action.options.expose_conf") + }, + { + "name": "password-image", + "description": lib_plankton.translate.get("help.args.action.options.password_image") + }, + { + "name": "admin-add", + "description": lib_plankton.translate.get("help.args.action.options.admin_add") + }, + { + "name": "export-authelia", + "description": lib_plankton.translate.get("help.args.action.options.export_authelia") + }, + { + "name": "help", + "description": lib_plankton.translate.get("help.args.action.options.help") + }, + ] + .map( + entry => lib_plankton.string.coin( + "\t\t- {{name}}\n\t\t\t{{description}}\n", + { + "name": entry.name, + "description": entry.description, + } + ) ) - ) - .join("") - ), - } - ), - }), - "arg1": lib_plankton.args.class_argument.positional({ - "index": 1, - "type": lib_plankton.args.enum_type.string, - "mode": lib_plankton.args.enum_mode.replace, - "default": null, - // "info": null, - "name": "arg1", - "hidden": true, - }), - "arg2": lib_plankton.args.class_argument.positional({ - "index": 2, - "type": lib_plankton.args.enum_type.string, - "mode": lib_plankton.args.enum_mode.replace, - "default": null, - // "info": null, - "name": "arg2", - "hidden": true, - }), - "arg3": lib_plankton.args.class_argument.positional({ - "index": 3, - "type": lib_plankton.args.enum_type.string, - "mode": lib_plankton.args.enum_mode.replace, - "default": null, - // "info": null, - "name": "arg3", - "hidden": true, - }), - "conf_path": lib_plankton.args.class_argument.volatile({ - "indicators_long": ["conf_path"], - "indicators_short": ["c"], - "type": lib_plankton.args.enum_type.string, - "mode": lib_plankton.args.enum_mode.replace, - "default": "conf.json", - "info": lib_plankton.translate.get("help.args.conf_path.description"), - "name": "conf-path", - }), - "help": lib_plankton.args.class_argument.volatile({ - "indicators_long": ["help"], - "indicators_short": ["h"], - "type": lib_plankton.args.enum_type.boolean, - "mode": lib_plankton.args.enum_mode.replace, - "default": false, - "info": lib_plankton.translate.get("help.args.help.description"), - "name": "help", - }), - }); - const args : Record = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" ")); - - // init - await _espe.conf.load(args["conf_path"]); - { - const language : (null | string) = _espe.conf.get().general.language; - if (language === null) { - // do nothing + .join("") + ), + } + ), + }), + "arg1": lib_plankton.args.class_argument.positional({ + "index": 1, + "type": lib_plankton.args.enum_type.string, + "mode": lib_plankton.args.enum_mode.replace, + "default": null, + // "info": null, + "name": "arg1", + "hidden": true, + }), + "arg2": lib_plankton.args.class_argument.positional({ + "index": 2, + "type": lib_plankton.args.enum_type.string, + "mode": lib_plankton.args.enum_mode.replace, + "default": null, + // "info": null, + "name": "arg2", + "hidden": true, + }), + "arg3": lib_plankton.args.class_argument.positional({ + "index": 3, + "type": lib_plankton.args.enum_type.string, + "mode": lib_plankton.args.enum_mode.replace, + "default": null, + // "info": null, + "name": "arg3", + "hidden": true, + }), + "conf_path": lib_plankton.args.class_argument.volatile({ + "indicators_long": ["conf_path"], + "indicators_short": ["c"], + "type": lib_plankton.args.enum_type.string, + "mode": lib_plankton.args.enum_mode.replace, + "default": "conf.json", + "info": lib_plankton.translate.get("help.args.conf_path.description"), + "name": "conf-path", + }), + "help": lib_plankton.args.class_argument.volatile({ + "indicators_long": ["help"], + "indicators_short": ["h"], + "type": lib_plankton.args.enum_type.boolean, + "mode": lib_plankton.args.enum_mode.replace, + "default": false, + "info": lib_plankton.translate.get("help.args.help.description"), + "name": "help", + }), + }); + const args : Record = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" ")); + + // init + await _espe.conf.load(args["conf_path"]); + { + const language : (null | string) = _espe.conf.get().general.language; + if (language === null) { + // do nothing + } + else { + lib_plankton.translate.promote(language); + } + } + setup_log_from_conf(); + + // exec + if (args["help"] || (args["action"] === "help")) { + process.stdout.write( + arg_handler.generate_help( + { + "programname": "espe", + "description": "Espe | Backend", + "executable": "espe", + } + ) + + + "\n" + ); } else { - lib_plankton.translate.promote(language); - } - } - lib_plankton.log.conf_push( - _espe.conf.get().log.map( - log_output => lib_plankton.log.channel_make( - { - "kind": log_output.kind, - "data": log_output.data + switch (args["action"]) { + default: { + process.stderr.write("invalid action: " + args["action"] + "\n"); + break; } - ) - ) - ); - - // exec - if (args["help"] || (args["action"] === "help")) { - process.stdout.write( - arg_handler.generate_help( - { - "programname": "espe", - "description": "Espe | Backend", - "executable": "espe", - } - ) - + - "\n" - ); - } - else { - switch (args["action"]) { - default: { - process.stderr.write("invalid action: " + args["action"] + "\n"); - break; - } - case "password-image": { - const input : (null | string) = args["arg1"]; - if (input === null) { - throw (new Error("SYNTAX: password-image ")); - } - else { - const result : string = await _espe.helpers.bcrypt_compute(input); - process.stdout.write(result + "\n") - } - break; - } - case "expose-conf": { - process.stdout.write( - JSON.stringify( - _espe.conf.get(), - undefined, - "\t" - ) - + - "\n" - ); - break; - } - case "api-doc": { - lib_plankton.log.conf_push([]); - const rest_subject : lib_plankton.rest.type_rest = _espe.api.make(); - lib_plankton.log.conf_pop(); - process.stdout.write( - JSON.stringify( - lib_plankton.rest.to_oas(rest_subject), - undefined, - "\t" - ) - ); - break; - } - case "email-test": { - await _espe.helpers.email_send( - ( - (args["arg1"] !== null) - ? [args["arg1"]] - : (await _espe.service.admin.email_addresses()) - ), - lib_plankton.string.coin( - "{{head}} | Test", - { - "head": _espe.conf.get().settings.organisation.name, - } - ), - "This is a test e-mail" - ); - break; - } - case "admin-add": { - const name : (null | string) = args["arg1"]; - const email_address : (null | string) = args["arg2"]; - const password : (null | string) = args["arg3"]; - if ((name === null) || (email_address === null) || (password === null)) { - throw (new Error("SYNTAX: admin-add ")); - } - else { - const admin_id : _espe.type.admin_id = await _espe.service.admin.add( - name, - email_address, - password - ); - process.stdout.write(admin_id.toFixed(0) + "\n") - } - break; - } - case "serve": { - // prepare database - await _espe.database.check(); - - await lib_plankton.session.setup( - { - "data_chest": ( - _espe.conf.get().session_management.in_memory - ? lib_plankton.storage.memory.implementation_chest({}) - : lib_plankton.call.convey( - lib_plankton.storage.sql_table_common.chest( - { - "database_implementation": _espe.helpers.database_implementation(), - "table_name": "sessions", - "key_names": ["key"], - } - ), - [ - (core) => ({ - "setup": (input) => core.setup(undefined), - "clear": () => core.clear(), - "write": (key, value) => core.write([key], {"data": JSON.stringify(value)}), - "delete": (key) => core.delete([key]), - "read": (key) => core.read([key]).then(row => JSON.parse(row["data"])), - // "search": (term) => core.search(term).then(() => []), - "search": (term) => Promise.reject(new Error("not implemented")), - }), - ] - ) - ), - "default_lifetime": _espe.conf.get().session_management.lifetime, + case "password-image": { + const input : (null | string) = args["arg1"]; + if (input === null) { + throw (new Error("SYNTAX: password-image ")); } - ); - - // outputs - _espe.service.member.listen_change( - () => { - lib_plankton.log.info( - "member_change", + else { + const result : string = await _espe.helpers.bcrypt_compute(input); + process.stdout.write(result + "\n") + } + break; + } + case "expose-conf": { + process.stdout.write( + JSON.stringify( + _espe.conf.get(), + undefined, + "\t" + ) + + + "\n" + ); + break; + } + case "api-doc": { + // lib_plankton.log.conf_push([]); + const rest_subject : lib_plankton.rest_http.type_rest = _espe.api.make(); + // lib_plankton.log.conf_pop(); + process.stdout.write( + JSON.stringify( + lib_plankton.rest_http.to_oas(rest_subject), + undefined, + "\t" + ) + ); + break; + } + case "email-test": { + await _espe.helpers.email_send( + ( + (args["arg1"] !== null) + ? [args["arg1"]] + : (await _espe.service.admin.email_addresses()) + ), + lib_plankton.string.coin( + "{{head}} | Test", { + "head": _espe.conf.get().settings.organisation.name, } + ), + "This is a test e-mail" + ); + break; + } + case "admin-add": { + const name : (null | string) = args["arg1"]; + const email_address : (null | string) = args["arg2"]; + const password : (null | string) = args["arg3"]; + if ((name === null) || (email_address === null) || (password === null)) { + throw (new Error("SYNTAX: admin-add ")); + } + else { + const admin_id : _espe.type.admin_id = await _espe.service.admin.add( + name, + email_address, + password ); - _espe.conf.get().outputs.forEach( - output_description => { - const procedure : (() => Promise) = ( - (() => { - switch (output_description.kind) { - default: { + process.stdout.write(admin_id.toFixed(0) + "\n") + } + break; + } + case "serve": { + // prepare database + await _espe.database.check(); + + await lib_plankton.session.setup( + { + "data_chest": ( + _espe.conf.get().session_management.in_memory + ? lib_plankton.storage.memory.implementation_chest({}) + : lib_plankton.call.convey( + lib_plankton.storage.sql_table_common.chest( + { + "database_implementation": _espe.helpers.database_implementation(), + "table_name": "sessions", + "key_names": ["key"], + } + ), + [ + (core) => ({ + "setup": (input) => core.setup(undefined), + "clear": () => core.clear(), + "write": (key, value) => core.write([key], {"data": JSON.stringify(value)}), + "delete": (key) => core.delete([key]), + "read": (key) => core.read([key]).then(row => JSON.parse(row["data"])), + // "search": (term) => core.search(term).then(() => []), + "search": (term) => Promise.reject(new Error("not implemented")), + }), + ] + ) + ), + "default_lifetime": _espe.conf.get().session_management.lifetime, + } + ); + + // outputs + _espe.service.member.listen_change( + () => { + lib_plankton.log.info( + "member_change", + { + } + ); + _espe.conf.get().outputs.forEach( + output_description => { + const procedure : (() => Promise) = ( + (() => { + switch (output_description.kind) { + default: { + lib_plankton.log.warning( + "output_kind_unhandled", + { + "description": output_description, + } + ); + return (() => Promise.resolve(undefined)); + break; + } + case "authelia_file": { + return (() => _espe.service.member.output_authelia_file(output_description.data)); + break; + } + case "http": { + return (() => _espe.service.member.output_http(output_description.data)); + break; + } + case "arc": { + return (() => _espe.service.member.output_arc(output_description.data)); + break; + } + } + }) () + ); + ( + procedure() + .then( + () => { + } + ) + .catch( + (error) => { lib_plankton.log.warning( - "output_kind_unhandled", + "output_procedure_failed", { - "description": output_description, + "error": String(error), } ); - return (() => Promise.resolve(undefined)); - break; } - case "authelia_file": { - return (() => _espe.service.member.output_authelia_file(output_description.data)); - break; - } - case "http": { - return (() => _espe.service.member.output_http(output_description.data)); - break; - } - case "arc": { - return (() => _espe.service.member.output_arc(output_description.data)); - break; - } - } - }) () - ); - ( - procedure() - .then( - () => { - } - ) - .catch( - (error) => { - lib_plankton.log.warning( - "output_procedure_failed", - { - "error": String(error), - } - ); - } - ) - ); - } - ); - } - ); - - const rest_subject : lib_plankton.rest.type_rest = _espe.api.make(); - const server : lib_plankton.server.type_subject = lib_plankton.server.make( - async (input, metadata) => { - const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(input.toString()); - const http_response : lib_plankton.http.type_response = await lib_plankton.rest.call( - rest_subject, - http_request, - { - "checklevel_restriction": lib_plankton.api.enum_checklevel.hard, - // "checklevel_input": lib_plankton.api.enum_checklevel.soft, - // "checklevel_output": lib_plankton.api.enum_checklevel.soft, - } - ); - const output : string = lib_plankton.http.encode_response(http_response); - return output; - }, - { - "host": _espe.conf.get().server.host, - "port": _espe.conf.get().server.port, - // DANGER! DANGER! - "threshold": 0.125, - } - ); - - lib_plankton.server.start(server); - break; - } - case "export-authelia": { - process.stdout.write(await _espe.service.member.export_authelia_user_file() + "\n"); - break; - } - } + ) + ); + } + ); + } + ); + + const rest_subject : lib_plankton.rest_http.type_rest = _espe.api.make(); + const server : lib_plankton.server.type_subject = lib_plankton.server.make( + async (input, metadata) => { + const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(input.toString()); + const http_response : lib_plankton.http.type_response = await lib_plankton.rest_http.call( + rest_subject, + http_request, + { + "checklevel_restriction": lib_plankton.api.enum_checklevel.hard, + // "checklevel_input": lib_plankton.api.enum_checklevel.soft, + // "checklevel_output": lib_plankton.api.enum_checklevel.soft, + } + ); + const output : string = lib_plankton.http.encode_response(http_response); + return output; + }, + { + "host": _espe.conf.get().server.host, + "port": _espe.conf.get().server.port, + // DANGER! DANGER! + "threshold": 0.125, + } + ); + + lib_plankton.server.start(server); + break; + } + case "export-authelia": { + process.stdout.write(await _espe.service.member.export_authelia_user_file() + "\n"); + break; + } + } + } } + } - ( - main(process.argv.slice(2)) + _espe.main(process.argv.slice(2)) .then( () => { } diff --git a/source/repositories/member.ts b/source/repositories/member.ts index fe67b14..e9e7398 100644 --- a/source/repositories/member.ts +++ b/source/repositories/member.ts @@ -156,7 +156,9 @@ namespace _espe.repository.member "email_address_private": dispersal.core_row["email_address_private"], "groups": lib_plankton.list.sorted( dispersal.group_rows.map(row => row["group_name"]), - (group1, group2) => ((group1 <= group2) ? 0 : 1) + { + "compare_element": (group1, group2) => (group1 <= group2) + } ), "registered": (dispersal.core_row["registered"] > 0), "enabled": (dispersal.core_row["enabled"] > 0), diff --git a/tools/update-plankton b/tools/update-plankton index 0be60a7..b4dbb8d 100755 --- a/tools/update-plankton +++ b/tools/update-plankton @@ -16,8 +16,8 @@ modules="${modules} storage" modules="${modules} session" modules="${modules} json" modules="${modules} api" -modules="${modules} rest" modules="${modules} http" +modules="${modules} rest_http" modules="${modules} server" modules="${modules} email" modules="${modules} args"