From 612e11b32e95493ca495cedc49be8e73cf273d78 Mon Sep 17 00:00:00 2001 From: Fenris Wolf Date: Thu, 25 Sep 2025 16:21:27 +0200 Subject: [PATCH] [mod] tools:update-plankton [upd] plankton --- lib/plankton/plankton.d.ts | 1035 +++++++---- lib/plankton/plankton.js | 3606 ++++++++++++++++++++---------------- tools/update-plankton | 19 +- 3 files changed, 2655 insertions(+), 2005 deletions(-) diff --git a/lib/plankton/plankton.d.ts b/lib/plankton/plankton.d.ts index 98a26b1..e094935 100644 --- a/lib/plankton/plankton.d.ts +++ b/lib/plankton/plankton.d.ts @@ -227,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 { /** @@ -355,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 */ @@ -548,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) * @@ -574,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) * @@ -582,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; /** @@ -610,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; @@ -621,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 * @@ -629,26 +624,36 @@ 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; /** */ - function try_catch_wrap(get_value: (() => type_value)): { + export function try_catch_wrap(get_value: (() => type_value)): { value: (null | type_value); error: (null | any); }; + /** + */ + export function try_catch_wrap_async(get_value: (() => Promise)): Promise<{ + value: (null | type_value); + error: (null | any); + }>; + /** + */ + export function sleep(seconds: float): Promise; + export {}; } declare namespace lib_plankton.email { /** @@ -670,183 +675,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 debug(incident: string, details?: Record): void; - /** - */ - function info(incident: string, details?: Record): void; - /** - */ - function notice(incident: string, details?: Record): void; - /** - */ - function warning(incident: string, details?: Record): void; - /** - */ - function error(incident: string, details?: Record): void; + function get_channel_logic(channel_description: type_channel_description): type_channel_logic; } declare namespace lib_plankton.log { + /** + */ + function default_logger(): type_logger_data; +} +declare namespace lib_plankton.log { + /** + */ + function set_main_logger(logger_data: type_logger_data): void; + /** + * consumes a log entry, i.e. sends it to all channels + */ + function send_(logger: type_logger_logic, entry: type_entry): void; + /** + * [convenience] + * + * @todo rename to "send" + */ + 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 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 namespace lib_plankton.code { /** @@ -1365,7 +1498,7 @@ declare namespace lib_plankton.conf { */ type type_schema = ({ enum?: Array; - default?: any; + default?: (null | any); description?: string; } | { type: "null"; @@ -1374,32 +1507,32 @@ declare namespace lib_plankton.conf { type: "boolean"; nullable?: boolean; enum?: Array; - default?: boolean; + default?: (null | boolean); description?: string; } | { type: "integer"; nullable?: boolean; enum?: Array; - default?: int; + default?: (null | int); description?: string; } | { type: "number"; nullable?: boolean; enum?: Array; - default?: number; + default?: (null | number); description?: string; } | { type: "string"; nullable?: boolean; enum?: Array; - default?: string; + default?: (null | string); description?: string; } | { type: "array"; nullable?: boolean; items: type_schema; enum?: Array>; - default?: Array; + default?: (null | Array); description?: string; } | { type: "object"; @@ -1408,7 +1541,7 @@ declare namespace lib_plankton.conf { required?: Array; additionalProperties?: (false | type_schema); enum?: Array>; - default?: Record; + default?: (Record | null); description?: string; } | { anyOf: Array; @@ -1420,6 +1553,12 @@ declare namespace lib_plankton.conf { } | { not: type_schema; }); + /** + */ + type type_sheet = { + version: (null | string); + content: type_content; + }; /** */ type type_report = { @@ -1432,15 +1571,24 @@ declare namespace lib_plankton.conf { reports: Array; result: lib_plankton.pod.type_pod; }; + /** + */ + type type_migration = (null | { + target: string; + function: ((content: type_from) => type_to); + }); } declare namespace lib_plankton.conf { /** - * @todo versioning */ - function refine(schema: type_schema, value_raw: any): type_result; + function refine(schema: type_schema, content: any): type_result; + /** + * @deprecated + */ + function load(schema: type_schema, path: (null | string)): Promise; /** */ - function load(schema: type_schema, path: (null | string)): Promise; + function load_versioned(path: string, get_schema: ((version: string) => type_schema), migrations: Record>): Promise>; } declare var plain_text_to_html: (text: string) => string; /** @@ -1478,7 +1626,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 */ @@ -1564,6 +1712,9 @@ declare namespace lib_plankton.string { /** */ function slice(str: string, size: int): Array; + /** + */ + function capitalize(str: string): string; } /** * @deprecated @@ -2362,95 +2513,47 @@ declare namespace lib_plankton.storage.sql_table_common { declare namespace lib_plankton.cache { /** */ - type type_cache = { - init: (() => Promise); - clear: (() => Promise); - query: ((key: string, retrieve: (() => Promise)) => Promise<{ - retrieved: boolean; - value: type_value; - }>); + type type_result = { + retrieved: boolean; + value: type_value; }; + /** + */ + type type_entry = { + value: type_value; + expiry: (null | float); + }; + /** + */ + type type_subject = lib_plankton.storage.type_chest, void, any, any>; } declare namespace lib_plankton.cache { /** */ - function get(cache: type_cache, key: string, retrieve: (() => Promise)): Promise; - /** - */ - function get_complex(cache: type_cache, group: string, input: type_input, retrieve: ((input: type_input) => Promise), options?: { - encode_input?: ((input: type_input) => string); - }): Promise; -} -declare namespace lib_plankton.cache.never { - /** - * @author fenris - */ - type type_subject = {}; - /** - * @author fenris - */ - function make(): type_subject; - /** - */ - function implementation(subject: type_subject): type_cache; -} -declare namespace lib_plankton.cache.always { - /** - * @author fenris - */ - type type_subject = { - value: lib_plankton.pod.type_pod; - }; - /** - * @author fenris - */ - function make(value: lib_plankton.pod.type_pod): type_subject; - /** - * @author fenris - */ - function clear(subject: type_subject): Promise; - /** - * @author fenris - */ - function query(subject: type_subject, key: string, retrieve: (() => Promise)): Promise<{ - retrieved: boolean; - value: type_value; - }>; - /** - */ - function implementation(subject: type_subject): type_cache; -} -declare namespace lib_plankton.cache.chest { - /** - * @author fenris - */ - type type_subject = { - chest: lib_plankton.storage.type_chest; - }; - /** - * @author fenris - */ - function make(options?: { - chest?: lib_plankton.storage.type_chest; + function make({ "chest": chest, }?: { + chest?: lib_plankton.storage.type_chest, void, any, any>; }): type_subject; /** - * @author fenris */ function init(subject: type_subject): Promise; /** - * @author fenris */ function clear(subject: type_subject): Promise; /** - * @author fenris */ - function query(subject: type_subject, key: string, retrieve: (() => Promise)): Promise<{ - retrieved: boolean; - value: type_value; - }>; + function invalidate(subject: type_subject, key: string): Promise; /** */ - function implementation(subject: type_subject): type_cache; + function query(subject: type_subject, key: string, lifetime: (null | float), retrieve: (() => Promise)): Promise>; + /** + * syntactic sugar: if the information, whether the value was retrieved, is irrelevant + */ + function get(subject: type_subject, key: string, lifetime: (null | float), retrieve: (() => Promise)): Promise; + /** + */ + function get_complex(cache: type_subject, group: string, input: type_input, lifetime: (null | float), retrieve: ((input: type_input) => Promise), { "encode_input": encode_input, }?: { + encode_input?: ((input: type_input) => string); + }): Promise; } declare namespace lib_plankton.shape { /** @@ -3095,7 +3198,7 @@ declare namespace lib_plankton.pit { /** * @todo test */ - function to_datetime(pit: type_pit, { timezone_shift, }?: { + function to_datetime(pit: type_pit, { "timezone_shift": option_timezone_shift, }?: { timezone_shift?: int; }): type_datetime; /** @@ -3104,18 +3207,45 @@ declare namespace lib_plankton.pit { /** */ function is_before(pit: type_pit, reference: type_pit): boolean; + /** + */ + function is_after(pit: type_pit, reference: type_pit): boolean; + /** + */ + function is_equal_or_after(pit: type_pit, reference: type_pit): boolean; /** */ function is_between(pit: type_pit, reference_left: type_pit, reference_right: type_pit): boolean; + /** + */ + function shift_hour(pit: type_pit, increment: int): type_pit; /** */ function shift_day(pit: type_pit, increment: int): type_pit; /** */ function shift_week(pit: type_pit, increment: int): type_pit; + /** + */ + function shift_year(pit: type_pit, increment: int): type_pit; + /** + */ + function trunc_minute(pit: type_pit): type_pit; + /** + */ + function trunc_hour(pit: type_pit): type_pit; + /** + */ + function trunc_day(pit: type_pit): type_pit; /** */ function trunc_week(pit: type_pit): type_pit; + /** + */ + function trunc_month(pit: type_pit): type_pit; + /** + */ + function trunc_year(pit: type_pit): type_pit; /** */ function now(): type_pit; @@ -3124,13 +3254,13 @@ declare namespace lib_plankton.pit { * @param week week according to specified timezone shift * @return the begin of the week (monday, 00:00) */ - function from_ywd(ywd: type_ywd, options?: { + function from_ywd(ywd: type_ywd, { "timezone_shift": option_timezone_shift, }?: { timezone_shift?: int; }): type_pit; /** * @todo timezone */ - function to_ywd(pit: type_pit, options?: { + function to_ywd(pit: type_pit, { "timezone_shift": option_timezone_shift, }?: { timezone_shift?: int; }): type_ywd; /** @@ -3167,13 +3297,13 @@ declare namespace lib_plankton.pit { function date_format(date: type_date): string; /** */ - function time_format(time: type_time, { show_seconds, }?: { + function time_format(time: type_time, { "show_seconds": option_show_seconds, }?: { show_seconds?: boolean; }): string; /** * @todo show timezone */ - function datetime_format(datetime: (null | type_datetime), { timezone_indicator, show_timezone, adjust_to_ce, omit_date, }?: { + function datetime_format(datetime: (null | type_datetime), { "timezone_indicator": option_timezone_indicator, "show_timezone": option_show_timezone, "adjust_to_ce": option_adjust_to_ce, "omit_date": option_omit_date, }?: { timezone_indicator?: string; show_timezone?: boolean; adjust_to_ce?: boolean; @@ -3181,7 +3311,7 @@ declare namespace lib_plankton.pit { }): string; /** */ - function timespan_format(from: type_datetime, to: (null | type_datetime), { timezone_indicator, show_timezone, adjust_to_ce, omit_date, }?: { + function timespan_format(from: type_datetime, to: (null | type_datetime), { "timezone_indicator": option_timezone_indicator, "show_timezone": option_show_timezone, "adjust_to_ce": option_adjust_to_ce, "omit_date": option_omit_date, }?: { timezone_indicator?: string; show_timezone?: boolean; adjust_to_ce?: boolean; @@ -3369,6 +3499,52 @@ declare namespace lib_plankton.ical { */ function ics_encode(vcalendar: type_vcalendar): 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 @@ -3383,33 +3559,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 */ @@ -3431,52 +3659,12 @@ 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.markdown { /** * @author fenris @@ -3490,6 +3678,10 @@ declare namespace lib_plankton.markdown { * @author fenris */ function sectionhead(level: int, content: string): string; + /** + * @author fenris + */ + function list(level: int, elements: Array): string; } declare namespace lib_plankton.api { /** @@ -3621,7 +3813,7 @@ declare namespace lib_plankton.api { generate_documentation(): string; } } -declare namespace lib_plankton.rest { +declare namespace lib_plankton.rest_base { /** */ type type_oas_schema = ({} | { @@ -3656,7 +3848,6 @@ declare namespace lib_plankton.rest { /** */ type type_execution = ((stuff: { - version: (null | string); headers: Record; path_parameters: Record; query_parameters: Record; @@ -3664,11 +3855,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; @@ -3677,22 +3868,22 @@ declare namespace lib_plankton.rest { */ type type_operation = { action_name: string; - query_parameters: ((version: string) => Array<{ + 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: 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); + 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; @@ -3715,16 +3906,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?: { @@ -3740,64 +4025,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?: ((version: string) => 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?: ((version: 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?: 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 @@ -3809,13 +4062,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; @@ -3825,7 +4076,6 @@ declare namespace lib_plankton.server { serverobj: any; }; /** - * @author fenris */ function make(handle: ((input: string, metadata?: type_metadata) => Promise), options?: { host?: string; @@ -3833,17 +4083,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; } @@ -4394,3 +4641,9 @@ declare namespace lib_plankton.auth.oidc { }>; export {}; } +declare namespace lib_plankton.sha256 { + /** + * @author fenris + */ + function get(value: string, secret?: string): string; +} diff --git a/lib/plankton/plankton.js b/lib/plankton/plankton.js index 35ee966..58e904a 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 = {})); /* @@ -776,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 . */ @@ -1388,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 */ @@ -1510,6 +1445,30 @@ var lib_plankton; } } 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; + /** + */ + function sleep(seconds) { + return (new Promise((resolve, reject) => { + setTimeout(() => { + resolve(undefined); + }, Math.floor(seconds * 1000)); + })); + } + call.sleep = sleep; })(call = lib_plankton.call || (lib_plankton.call = {})); })(lib_plankton || (lib_plankton = {})); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { @@ -1606,21 +1565,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«. @@ -1655,6 +1599,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) { @@ -1663,469 +1632,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, @@ -2134,43 +1667,587 @@ 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) { - default: { - throw (new Error("unhandled log channel kind: " + 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 "stdout": { - return (new log.class_channel_minlevel(new log.class_channel_stdout(), translate_level((_a = description.data["threshold"]) !== null && _a !== void 0 ? _a : "debug"))); + 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 format kind: " + format_definition["kind"])); + break; + } + } + } + log.format_entry = format_entry; + /** + */ + 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.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 = {})); +/* +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 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": { - 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"))); + /** + * @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 (new log.class_channel_minlevel(new log.class_channel_notify(), translate_level((_e = description.data["threshold"]) !== null && _e !== void 0 ? _e : "debug"))); + 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.channel_make = channel_make; - /** - */ - 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), - ]; - } - log.conf_default = conf_default; + log.get_channel_logic = get_channel_logic; })(log = lib_plankton.log || (lib_plankton.log = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -2198,122 +2275,288 @@ 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); + function default_logger() { + return [ + { + "kind": "minlevel", + "data": { + "core": { + "kind": "std", + "data": { + "target": "stdout", + "format": { + "kind": "human_readable", + "data": {} + } + } + }, + "threshold": "info" + } + }, + ]; } - log.conf_push = conf_push; + 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) { /** - * pops the current active configuration from the stack */ - function conf_pop() { - if (_channel_stack.length > 0) { - _channel_stack.pop(); - } - else { - // do nothing - } + var _main_logger_data = null; + /** + */ + function set_main_logger(logger_data) { + _main_logger_data = logger_data; } - log.conf_pop = conf_pop; + log.set_main_logger = set_main_logger; /** - * makes the logging system ready */ - function setup() { - if (_channel_stack === null) { - _channel_stack = []; - conf_push(log.conf_default()); - } - else { - // do nothing - } + 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 = {})); })(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) { - /** - */ - log.conf_push([ - log.channel_make({ - "kind": "stdout", - "data": { - "threshold": "info" - } - }), - ]); - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* This file is part of »bacterio-plankton:code«. Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' @@ -4309,7 +4552,7 @@ var lib_plankton; .map((report_entry) => ({ "incident": report_entry.incident, "details": Object.assign(report_entry.details, { - "path": (report_entry.details.path ?? []).concat([entry.right]) + "path": [entry.right].concat(report_entry.details.path ?? []), }), }))); } @@ -4340,7 +4583,7 @@ var lib_plankton; .map((report_entry) => ({ "incident": report_entry.incident, "details": Object.assign(report_entry.details, { - "path": (report_entry.details.path ?? []).concat([entry.left]) + "path": [entry.left].concat(report_entry.details.path ?? []), }), }))); } @@ -4358,7 +4601,7 @@ var lib_plankton; .map((report_entry) => ({ "incident": report_entry.incident, "details": Object.assign(report_entry.details, { - "path": (report_entry.details.path ?? []).concat([entry.right]) + "path": [entry.right].concat(report_entry.details.path ?? []), }), }))); } @@ -4390,10 +4633,9 @@ var lib_plankton; } } /** - * @todo versioning */ - function refine(schema, value_raw) { - const adaption = adapt(schema, value_raw); + function refine(schema, content) { + const adaption = adapt(schema, content); if (!lib_plankton.pod.is_filled(adaption.result)) { throw (new Error("conf could not be loaded:\n" + @@ -4407,6 +4649,7 @@ var lib_plankton; } conf.refine = refine; /** + * @deprecated */ function load(schema, path) { return (((path === null) @@ -4418,6 +4661,37 @@ var lib_plankton; .then((data_raw) => Promise.resolve(refine(schema, data_raw)))); } conf.load = load; + /** + */ + async function load_versioned(path, get_schema, migrations) { + const file_content = await lib_plankton.file.read(path); + const sheet_raw = lib_plankton.json.decode(file_content); + const schema = get_schema(sheet_raw.version); + let sheet = { + "version": sheet_raw.version, + "content": refine(schema, sheet_raw.content), + }; + while (sheet.version in migrations) { + const migration = migrations[sheet.version]; + if (!(migration === null)) { + lib_plankton.log._info("plankton.conf.load.migration", { + "details": { + "from": sheet.version, + "to": migration.target, + } + }); + sheet = { + "version": migration.target, + "content": migration.function(sheet.content), + }; + } + else { + break; + } + } + return sheet; + } + conf.load_versioned = load_versioned; })(conf = lib_plankton.conf || (lib_plankton.conf = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -4629,6 +4903,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 { @@ -4655,7 +4930,7 @@ var lib_plankton; * @return {Array} * @author fenris */ - function split(chain, separator = " ") { + function split(chain, separator) { if (chain.length == 0) { return []; } @@ -4929,6 +5204,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 = {})); /** @@ -7435,246 +7716,93 @@ var lib_plankton; (function (cache_1) { /** */ - function get(cache, key, retrieve) { - return cache.query(key, retrieve).then(result => result.value); + function make({ "chest": chest = lib_plankton.storage.memory.implementation_chest({}), } = {}) { + return chest; + } + cache_1.make = make; + /** + */ + function init(subject) { + return subject.setup(undefined); + } + cache_1.init = init; + /** + */ + function clear(subject) { + return subject.clear(); + } + cache_1.clear = clear; + /** + */ + function invalidate(subject, key) { + return subject.delete(key); + } + cache_1.invalidate = invalidate; + /** + */ + async function query(subject, key, lifetime, retrieve) { + const now = lib_plankton.base.get_current_timestamp(); + return ((subject.read(key) + .then((entry) => Promise.resolve({ + "found": true, + "entry": entry, + })) + .catch(() => Promise.resolve({ + "found": false, + "entry": null, + }))) + .then((item) => { + if ((!item.found) + || + ((item.entry.expiry !== null) + && + (item.entry.expiry <= now))) { + lib_plankton.log.info("plankton.cache.unknown_or_expired", { + "key": key, + }); + return (retrieve() + .then((value) => (subject.write(key, { + "value": value, + "expiry": ((lifetime === null) + ? + null + : + (now + lifetime)) + }) + .then(() => Promise.resolve({ + "retrieved": true, + "value": value + }))))); + } + else { + lib_plankton.log.info("plankton.cache.known_and_valid", { + "key": key, + }); + return Promise.resolve({ + "retrieved": false, + "value": item.entry.value + }); + } + })); + } + cache_1.query = query; + /** + * syntactic sugar: if the information, whether the value was retrieved, is irrelevant + */ + function get(subject, key, lifetime, retrieve) { + return (query(subject, key, lifetime, retrieve) + .then(result => Promise.resolve(result.value))); } cache_1.get = get; /** */ - function get_complex(cache, group, input, retrieve, options = {}) { - options = Object.assign({ - "encode_input": input => JSON.stringify(input), - }, options); - return get(cache, (group + "." + options.encode_input(input)), () => retrieve(input)); + function get_complex(cache, group, input, lifetime, retrieve, { "encode_input": encode_input = (input => JSON.stringify(input)), } = {}) { + return get(cache, (group + "." + encode_input(input)), lifetime, () => retrieve(input)); } cache_1.get_complex = get_complex; })(cache = lib_plankton.cache || (lib_plankton.cache = {})); })(lib_plankton || (lib_plankton = {})); /* -This file is part of »bacterio-plankton:cache«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:cache« 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:cache« 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:cache«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var cache; - (function (cache) { - var never; - (function (never) { - /** - * @author fenris - */ - function make() { - return {}; - } - never.make = make; - /** - * @author fenris - */ - function clear(subject) { - return Promise.resolve(undefined); - } - /** - * @author fenris - */ - async function query(subject, key, retrieve) { - return { - "retrieved": true, - "value": (await retrieve()), - }; - } - /** - */ - function implementation(subject) { - return { - "init": () => Promise.resolve(undefined), - "clear": () => clear(subject), - "query": (key, retrieve) => query(subject, key, retrieve), - }; - } - never.implementation = implementation; - })(never = cache.never || (cache.never = {})); - })(cache = lib_plankton.cache || (lib_plankton.cache = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:cache«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:cache« 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:cache« 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:cache«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var cache; - (function (cache) { - var always; - (function (always) { - /** - * @author fenris - */ - function make(value) { - return { - "value": value, - }; - } - always.make = make; - /** - * @author fenris - */ - function clear(subject) { - return Promise.resolve(undefined); - } - always.clear = clear; - /** - * @author fenris - */ - function query(subject, key, retrieve) { - if (lib_plankton.pod.is_filled(subject.value)) { - return Promise.resolve({ - "retrieved": false, - "value": lib_plankton.pod.cull(subject.value), - }); - } - else { - return Promise.reject(); - } - } - always.query = query; - /** - */ - function implementation(subject) { - return { - "init": () => Promise.resolve(undefined), - "clear": () => clear(subject), - "query": (key, retrieve) => query(subject, key, retrieve), - }; - } - always.implementation = implementation; - })(always = cache.always || (cache.always = {})); - })(cache = lib_plankton.cache || (lib_plankton.cache = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:cache«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:cache« 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:cache« 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:cache«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var cache; - (function (cache) { - var chest; - (function (chest) { - /** - * @author fenris - */ - function make(options = {}) { - options = Object.assign({ - "chest": lib_plankton.storage.memory.implementation_chest({}), - }, options); - return { - "chest": options.chest, - }; - } - chest.make = make; - /** - * @author fenris - */ - function init(subject) { - return subject.chest.setup(undefined); - } - chest.init = init; - /** - * @author fenris - */ - function clear(subject) { - return subject.chest.clear(); - } - chest.clear = clear; - /** - * @author fenris - */ - function query(subject, key, retrieve) { - return (subject.chest.read(key) - .then(value => (lib_plankton.log.info("cache.chest_hashed.known", { - "key": key, - }) - , - Promise.resolve({ - "retrieved": false, - "value": value, - }))) - .catch(() => (lib_plankton.log.info("cache.chest_hashed.unknown", { - "key": key, - }) - , - (retrieve() - .then((value) => (subject.chest.write(key, value) - .then(() => Promise.resolve({ - "retrieved": true, - "value": value, - })))) - /* - .catch( - (reason) => Promise.reject<{retrieved : boolean; value : type_value}>(reason) - ) - */ - )))); - } - chest.query = query; - /** - */ - function implementation(subject) { - return { - "init": () => init(subject), - "clear": () => clear(subject), - "query": (key, retrieve) => query(subject, key, retrieve), - }; - } - chest.implementation = implementation; - })(chest = cache.chest || (cache.chest = {})); - })(cache = lib_plankton.cache || (lib_plankton.cache = {})); -})(lib_plankton || (lib_plankton = {})); -/* This file is part of »bacterio-plankton:shape«. Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' @@ -10647,15 +10775,15 @@ var lib_plankton; /** * @todo test */ - function to_datetime(pit, { timezone_shift = 0, } = {}) { + function to_datetime(pit, { "timezone_shift": option_timezone_shift = 0, } = {}) { return lib_plankton.call.convey(pit, [ to_date_object, (x) => x.getTime(), - (x) => (x + ((timezone_shift * (60 * 60)) * 1000)), + (x) => (x + ((option_timezone_shift * (60 * 60)) * 1000)), (x) => new Date(x), x => x.toISOString(), x => ({ - "timezone_shift": timezone_shift, + "timezone_shift": option_timezone_shift, "date": { "year": parseInt(x.slice(0, 4)), "month": parseInt(x.slice(5, 7)), @@ -10699,10 +10827,17 @@ var lib_plankton; function is_after(pit, reference) { return (pit > reference); } + pit_1.is_after = is_after; + /** + */ + function is_equal_or_after(pit, reference) { + return (pit >= reference); + } + pit_1.is_equal_or_after = is_equal_or_after; /** */ function is_between(pit, reference_left, reference_right) { - return (is_after(pit, reference_left) + return (is_equal_or_after(pit, reference_left) && is_before(pit, reference_right)); } @@ -10712,6 +10847,7 @@ var lib_plankton; function shift_hour(pit, increment) { return (pit + (60 * 60 * increment)); } + pit_1.shift_hour = shift_hour; /** */ function shift_day(pit, increment) { @@ -10729,6 +10865,7 @@ var lib_plankton; function shift_year(pit, increment) { return (pit + (60 * 60 * 24 * 365 * increment)); } + pit_1.shift_year = shift_year; /** */ function trunc_minute(pit) { @@ -10756,6 +10893,7 @@ var lib_plankton; }; return from_datetime(datetime_output); } + pit_1.trunc_minute = trunc_minute; /** */ function trunc_hour(pit) { @@ -10779,6 +10917,7 @@ var lib_plankton; }; return from_datetime(datetime_output); } + pit_1.trunc_hour = trunc_hour; /** */ function trunc_day(pit) { @@ -10798,6 +10937,7 @@ var lib_plankton; }; return from_datetime(datetime_output); } + pit_1.trunc_day = trunc_day; /** */ function trunc_week(pit) { @@ -10829,6 +10969,7 @@ var lib_plankton; }; return from_datetime(datetime_output); } + pit_1.trunc_month = trunc_month; /** */ function trunc_year(pit) { @@ -10848,6 +10989,7 @@ var lib_plankton; }; return from_datetime(datetime_output); } + pit_1.trunc_year = trunc_year; /** */ function now() { @@ -10859,12 +11001,9 @@ var lib_plankton; * @param week week according to specified timezone shift * @return the begin of the week (monday, 00:00) */ - function from_ywd(ywd, options = {}) { - options = Object.assign({ - "timezone_shift": 0, - }, options); + function from_ywd(ywd, { "timezone_shift": option_timezone_shift = 0, } = {}) { return lib_plankton.call.convey({ - "timezone_shift": options.timezone_shift, + "timezone_shift": option_timezone_shift, "date": { "year": ywd.year, "month": 1, @@ -10886,10 +11025,7 @@ var lib_plankton; /** * @todo timezone */ - function to_ywd(pit, options = {}) { - options = Object.assign({ - "timezone_shift": 0, - }, options); + function to_ywd(pit, { "timezone_shift": option_timezone_shift = 0, } = {}) { return lib_plankton.call.convey(pit, [ to_date_object, x => ({ @@ -11027,8 +11163,8 @@ var lib_plankton; pit_1.date_format = date_format; /** */ - function time_format(time, { show_seconds = false, } = {}) { - return lib_plankton.string.coin((show_seconds + function time_format(time, { "show_seconds": option_show_seconds = false, } = {}) { + return lib_plankton.string.coin((option_show_seconds ? "{{hour}}:{{minute}}:{{seconds}}" : @@ -11042,12 +11178,12 @@ var lib_plankton; /** * @todo show timezone */ - function datetime_format(datetime, { timezone_indicator = "", show_timezone = false, adjust_to_ce = false, omit_date = false, } = {}) { + function datetime_format(datetime, { "timezone_indicator": option_timezone_indicator = "", "show_timezone": option_show_timezone = false, "adjust_to_ce": option_adjust_to_ce = false, "omit_date": option_omit_date = false, } = {}) { if (datetime === null) { return "-"; } else { - const datetime_adjusted = (adjust_to_ce + const datetime_adjusted = (option_adjust_to_ce ? to_datetime_ce(from_datetime(datetime)) : @@ -11055,7 +11191,7 @@ var lib_plankton; return lib_plankton.string.coin("{{macro_date_and_time}}{{macro_timezone}}", { "macro_date_and_time": ([ // date - (omit_date + (option_omit_date ? null : @@ -11069,10 +11205,10 @@ var lib_plankton; ] .filter(x => (x !== null)) .join(", ")), - "macro_timezone": (show_timezone + "macro_timezone": (option_show_timezone ? lib_plankton.string.coin(" [{{timezone_indicator}}{{timezone_value}}]", { - "timezone_indicator": timezone_indicator, + "timezone_indicator": option_timezone_indicator, "timezone_value": timezone_shift_format(datetime_adjusted.timezone_shift), }) : @@ -11083,8 +11219,8 @@ var lib_plankton; pit_1.datetime_format = datetime_format; /** */ - function timespan_format(from, to, { timezone_indicator = "", show_timezone = false, adjust_to_ce = false, omit_date = false, } = {}) { - const from_adjusted = (adjust_to_ce + function timespan_format(from, to, { "timezone_indicator": option_timezone_indicator = "", "show_timezone": option_show_timezone = false, "adjust_to_ce": option_adjust_to_ce = false, "omit_date": option_omit_date = false, } = {}) { + const from_adjusted = (option_adjust_to_ce ? datetime_translate_ce(from) : @@ -11093,7 +11229,7 @@ var lib_plankton; ? null : - (adjust_to_ce + (option_adjust_to_ce ? datetime_translate_ce(to) : @@ -11102,7 +11238,7 @@ var lib_plankton; "from": datetime_format(from_adjusted, { "show_timezone": false, "adjust_to_ce": false, - "omit_date": omit_date, + "omit_date": option_omit_date, }), "to_macro": ((to_adjusted === null) ? @@ -11117,10 +11253,10 @@ var lib_plankton; (from_adjusted.date.day === to_adjusted.date.day)) }), })), - "macro_timezone": (show_timezone + "macro_timezone": (option_show_timezone ? lib_plankton.string.coin(" [{{timezone_indicator}}{{timezone_value}}]", { - "timezone_indicator": timezone_indicator, + "timezone_indicator": option_timezone_indicator, "timezone_value": timezone_shift_format(from_adjusted.timezone_shift), }) : @@ -12047,6 +12183,12 @@ var lib_plankton; "url": vevent.url, })); } + // categories + if ((vevent.categories !== undefined) && (vevent.categories.length > 0)) { + content_lines.push(lib_plankton.string.coin("CATEGORIES:{{categories}}", { + "categories": vevent.categories.join(","), + })); + } } content_lines.push("END:VEVENT"); }); @@ -12062,6 +12204,339 @@ var lib_plankton; ical.ics_encode = ics_encode; })(ical = lib_plankton.ical || (lib_plankton.ical = {})); })(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«. @@ -12098,6 +12573,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 = {})); /* @@ -12123,22 +12657,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 */ @@ -12170,10 +12688,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"; @@ -12232,140 +12762,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; /** @@ -12373,241 +12806,17 @@ 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 = {})); /* -This file is part of »bacterio-plankton:http«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton: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: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:http«. 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:markdown«. Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' @@ -12658,6 +12867,18 @@ var lib_plankton; }); } markdown.sectionhead = sectionhead; + /** + * @author fenris + */ + function list(level, elements) { + return (elements + .map(function (element) { return lib_plankton.string.coin("{{spaces}} {{content}}\n", { + "spaces": lib_plankton.string.repeat(" ", level), + "content": element + }); }) + .join("")); + } + markdown.list = list; })(markdown = lib_plankton.markdown || (lib_plankton.markdown = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -13016,62 +13237,49 @@ 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:rest«. +This file is part of »bacterio-plankton:rest_base«. Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' -»bacterio-plankton:rest« 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:rest« 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:rest«. If not, see . +along with »bacterio-plankton:rest_base«. If not, see . */ /* -This file is part of »bacterio-plankton:rest«. +This file is part of »bacterio-plankton:rest_base«. Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' -»bacterio-plankton:rest« 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:rest« 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:rest«. If not, see . +along with »bacterio-plankton:rest_base«. 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) { @@ -13110,11 +13318,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, }; @@ -13122,7 +13330,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 @@ -13173,7 +13381,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, @@ -13199,21 +13407,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]; @@ -13228,7 +13437,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), }; } } @@ -13236,7 +13445,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, } @@ -13246,14 +13455,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 @@ -13261,7 +13470,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); } } } @@ -13269,141 +13478,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)), - "title": null, - "description": null, - "query_parameters": ((version) => ([])), - "input_schema": ((version) => ({})), - "output_schema": ((version) => ({})), - "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; @@ -13416,7 +13614,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; @@ -13431,13 +13629,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: { @@ -13455,81 +13663,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, @@ -13537,14 +13761,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( { @@ -13555,36 +13786,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, @@ -13593,53 +13817,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": { @@ -13647,18 +13900,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": [ { @@ -13679,24 +13932,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, @@ -13704,12 +13964,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, @@ -13717,9 +13977,10 @@ var lib_plankton; "type": "string", }, })), - ]), - // query parameters - operation.query_parameters(options.version).map((query_parameter) => ({ + ])) + // query parameters + .concat(operation.query_parameters(option_version) + .map((query_parameter) => ({ "name": query_parameter.name, "in": "query", "required": query_parameter.required, @@ -13727,19 +13988,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), } ] ]) @@ -13751,7 +14008,7 @@ var lib_plankton; [ operation.response_body_mimetype, { - "schema": operation.output_schema(options.version), + "schema": operation.output_schema(option_version), } ] ]), @@ -13763,13 +14020,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«. @@ -13795,7 +14143,6 @@ var lib_plankton; var server; (function (server) { /** - * @author fenris */ function make(handle, options = {}) { options = Object.assign({ @@ -13813,7 +14160,6 @@ var lib_plankton; } server.make = make; /** - * @author fenris * @deprecated */ function make_old(port, handle) { @@ -13825,7 +14171,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) { @@ -13846,20 +14191,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(); @@ -13873,7 +14219,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; } @@ -13885,21 +14231,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) @@ -13913,12 +14263,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 } }); @@ -13928,7 +14278,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, }); @@ -13938,11 +14288,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 = {})); @@ -15787,3 +16136,38 @@ var lib_plankton; })(oidc = auth.oidc || (auth.oidc = {})); })(auth = lib_plankton.auth || (lib_plankton.auth = {})); })(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:sha256«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:sha256« 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:sha256« 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:sha256«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var sha256; + (function (sha256) { + /** + * @author fenris + */ + function get(value, secret = "") { + const nm_crypto = require("crypto"); + const sha256Hasher = nm_crypto.createHmac("sha256", secret); + const hash = sha256Hasher.update(value).digest("hex"); + return hash; + } + sha256.get = get; + })(sha256 = lib_plankton.sha256 || (lib_plankton.sha256 = {})); +})(lib_plankton || (lib_plankton = {})); diff --git a/tools/update-plankton b/tools/update-plankton index beecb08..535aa95 100755 --- a/tools/update-plankton +++ b/tools/update-plankton @@ -2,7 +2,7 @@ ## consts -dir=lib/plankton +dir=$(pwd)/lib/plankton modules="" modules="${modules} base" @@ -21,19 +21,32 @@ modules="${modules} order" modules="${modules} ical" modules="${modules} url" modules="${modules} http" +# modules="${modules} webdav" +# modules="${modules} caldav" modules="${modules} api" -modules="${modules} rest" -modules="${modules} rest" +modules="${modules} rest_http" +# modules="${modules} rest_webdav" +# modules="${modules} rest_caldav" modules="${modules} server" modules="${modules} args" modules="${modules} bcrypt" modules="${modules} map" modules="${modules} pit" modules="${modules} auth" +modules="${modules} sha256" +modules="${modules} random" ## exec +mkdir -p ${dir} +mkdir /tmp/sandbox -p +cd /tmp/sandbox +ptk fetch node ${modules} +schwamm --include=/tmp/sandboxplankton.swm.json --output=dump:logic-decl > ${dir}/plankton.d.ts +schwamm --include=/tmp/sandboxplankton.swm.json --output=dump:logic-impl > ${dir}/plankton.js +exit + mkdir -p ${dir} cd ${dir} ptk bundle node ${modules}