From 1127130b00653de426401386ca16337f8ead64c5 Mon Sep 17 00:00:00 2001 From: Fenris Wolf Date: Sun, 27 Oct 2024 19:01:33 +0100 Subject: [PATCH 01/30] [task-192] [add] api:action:export_caldav --- source/api/actions/export_caldav.ts | 166 ++++++++++++++++++++++++++++ source/api/functions.ts | 4 + source/conf.ts | 15 +++ source/helpers.ts | 105 +++++++++++++++++- tools/makefile | 1 + 5 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 source/api/actions/export_caldav.ts diff --git a/source/api/actions/export_caldav.ts b/source/api/actions/export_caldav.ts new file mode 100644 index 0000000..d970b6e --- /dev/null +++ b/source/api/actions/export_caldav.ts @@ -0,0 +1,166 @@ + +namespace _zeitbild.api +{ + + /** + */ + export function register_export_caldav( + rest_subject : lib_plankton.rest.type_rest + ) : void + { + register< + null, + ( + lib_plankton.ical.type_vcalendar + | + string + ) + >( + rest_subject, + lib_plankton.http.enum_method.get, + "/export/caldav", + { + "description": "trägt Veranstaltungen aus verschiedenen Kalendern zusammen im CalDAV-Format", + "query_parameters": () => ([ + { + "name": "from", + "required": false, + "description": "UNIX timestamp", + }, + { + "name": "to", + "required": false, + "description": "UNIX timestamp", + }, + { + "name": "calendar_ids", + "required": false, + "description": "comma separated", + }, + { + "name": "auth", + "required": true, + "description": "", + }, + ]), + "output_schema": () => ({ + "nullable": false, + "type": "string", + }), + "response_body_mimetype": "text/calendar", + "response_body_encode": (output) => Buffer.from( + (typeof(output) === "string") + ? + output + : + lib_plankton.ical.ics_encode(output) + ), + "restriction": restriction_none, + "execution": async (stuff) => { + const user_id : (null | _zeitbild.type_user_id) = await ( + session_from_stuff(stuff) + .then( + (session : {key : string; value : lib_plankton.session.type_session;}) => ( + _zeitbild.service.user.identify(session.value.name) + .catch(x => Promise.resolve(null)) + ) + ) + .catch(x => Promise.resolve(null)) + ); + + const from : lib_plankton.pit.type_pit = ( + ("from" in stuff.query_parameters) + ? + parseInt(stuff.query_parameters["from"]) + : + lib_plankton.pit.shift_week( + lib_plankton.pit.now(), + -2 + ) + ); + const to : lib_plankton.pit.type_pit = ( + ("to" in stuff.query_parameters) + ? + parseInt(stuff.query_parameters["to"]) + : + lib_plankton.pit.shift_week( + lib_plankton.pit.now(), + +6 + ) + ); + const calendar_ids_wanted : (null | Array<_zeitbild.type_calendar_id>) = ( + ( + ("calendar_ids" in stuff.query_parameters) + && + (stuff.query_parameters["calendar_ids"] !== null) + ) + ? + lib_plankton.call.convey( + stuff.query_parameters["calendar_ids"], + [ + (x : string) => x.split(","), + (x : Array) => x.map(parseInt), + (x : Array) => x.filter(y => (! isNaN(y))) + ] + ) + : + null + ); + + const auth_hash_shall : string = lib_plankton.sha256.get( + (stuff.query_parameters["calendar_ids"] ?? ""), + _zeitbild.conf.get()["misc"]["auth_salt"] + ); + const auth_hash_is : string = stuff.query_parameters["auth"]; + /** + * @todo remove + */ + lib_plankton.log.info( + "auth_hashes", + { + "shall": auth_hash_shall, + "is": auth_hash_is, + } + ); + if (! (auth_hash_is === auth_hash_shall)) { + return Promise.resolve( + { + "status_code": 403, + "data": "not authorized", + } + ); + } + else { + return ( + _zeitbild.service.calendar.gather_events( + calendar_ids_wanted, + from, + to, + user_id + ) + .then( + (data) => Promise.resolve( + { + "status_code": 200, + "data": _zeitbild.helpers.ical_vcalendar_from_own_event_list( + data.map(entry => entry.event_object) + ) + } + ) + ) + .catch( + (reason) => Promise.resolve( + { + "status_code": 403, + "data": String(reason), + } + ) + ) + ); + } + } + } + ); + } + +} diff --git a/source/api/functions.ts b/source/api/functions.ts index 28ebbe2..1d24611 100644 --- a/source/api/functions.ts +++ b/source/api/functions.ts @@ -46,6 +46,10 @@ namespace _zeitbild.api _zeitbild.api.register_calendar_event_remove(rest_subject); } } + // export + { + _zeitbild.api.register_export_caldav(rest_subject); + } // misc { _zeitbild.api.register_users(rest_subject); diff --git a/source/conf.ts b/source/conf.ts index 537d357..ebb56d7 100644 --- a/source/conf.ts +++ b/source/conf.ts @@ -272,6 +272,21 @@ namespace _zeitbild.conf "data": { } } + }, + "misc": { + "nullable": false, + "type": "object", + "properties": { + "auth_salt": { + "nullable": false, + "type": "string", + "default": "unsafe_auth_salt" + } + }, + "required": [ + ], + "additionalProperties": false, + "default": {} } }, "required": [ diff --git a/source/helpers.ts b/source/helpers.ts index d5b929d..94e7cb5 100644 --- a/source/helpers.ts +++ b/source/helpers.ts @@ -31,8 +31,8 @@ namespace _zeitbild.helpers ) }; } - - + + /** * @todo timezone */ @@ -58,6 +58,107 @@ namespace _zeitbild.helpers } + /** + * @todo timezone + */ + export function ical_dt_from_own_datetime( + datetime : lib_plankton.pit.type_datetime + ) : lib_plankton.ical.type_dt + { + return { + "tzid": "Europe/Berlin", + "value": { + "date": { + "year": datetime.date.year, + "month": datetime.date.month, + "day": datetime.date.day, + }, + "time": ( + (datetime.time === null) + ? + null + : + { + "utc": true, + "hour": datetime.time.hour, + "minute": datetime.time.minute, + "second": datetime.time.second, + } + ) + }, + }; + } + + + /** + */ + export function ical_vevent_from_own_event( + event_object : _zeitbild.type_event_object, + uid : string + ) : lib_plankton.ical.type_vevent + { + return { + "uid": uid, + "dtstamp": ical_dt_from_own_datetime(event_object.begin).value, + "dtstart": ical_dt_from_own_datetime(event_object.begin), + "dtend": ( + (event_object.end === null) + ? + undefined + : + ical_dt_from_own_datetime(event_object.end) + ), + "location": (event_object.location ?? undefined), + "summary": event_object.name, + "url": (event_object.link ?? undefined), + "description": (event_object.description ?? undefined), + }; + } + + + /** + * @todo assign better uids + */ + export function ical_vcalendar_from_own_event_list( + events : Array<_zeitbild.type_event_object> + ) : lib_plankton.ical.type_vcalendar + { + const pit_now : lib_plankton.pit.type_pit = lib_plankton.pit.now(); + const datetime_now : lib_plankton.pit.type_datetime = lib_plankton.pit.to_datetime(pit_now); + const stamp : string = lib_plankton.string.coin( + "{{year}}{{month}}{{day}}", + { + "year": datetime_now.date.year.toFixed(0).padStart(4, "0"), + "month": datetime_now.date.month.toFixed(0).padStart(2, "0"), + "day": datetime_now.date.day.toFixed(0).padStart(2, "0"), + } + ); + return { + "version": "2.0", + "prodid": "", + "vevents": ( + events + .map( + (entry, index) => ical_vevent_from_own_event( + entry, + lib_plankton.string.coin( + "zeitbild_{{stamp}}_{{index}}", + { + "stamp": stamp, + "index": index.toFixed(0) + } + ) + ), + ) + ), + "method": "PUBLISH", + "vtimezone": { + "tzid": "Europe/Berlin", + }, + }; + } + + /** */ var _template_cache : Record = {}; diff --git a/tools/makefile b/tools/makefile index 1c2da4c..b5ea7a9 100644 --- a/tools/makefile +++ b/tools/makefile @@ -75,6 +75,7 @@ ${dir_temp}/zeitbild-unlinked.js: \ ${dir_source}/api/actions/calendar_event_change.ts \ ${dir_source}/api/actions/calendar_event_remove.ts \ ${dir_source}/api/actions/events.ts \ + ${dir_source}/api/actions/export_caldav.ts \ ${dir_source}/api/functions.ts \ ${dir_source}/main.ts @ ${cmd_log} "compile …" -- 2.47.3 From 9d1fd0b55f68ea324113bfe64af700d7706c91ef Mon Sep 17 00:00:00 2001 From: Fenris Wolf Date: Wed, 30 Oct 2024 07:20:13 +0100 Subject: [PATCH 02/30] [int] --- lib/plankton/plankton.d.ts | 595 ++++++++- lib/plankton/plankton.js | 1158 ++++++++++++++++- source/api/actions/caldav_get.ts | 166 +++ source/api/actions/caldav_probe.ts | 100 ++ source/api/actions/calendar_add.ts | 2 +- source/api/actions/calendar_change.ts | 2 +- source/api/actions/calendar_event_add.ts | 2 +- source/api/actions/calendar_event_change.ts | 2 +- source/api/actions/calendar_event_get.ts | 2 +- source/api/actions/calendar_event_remove.ts | 2 +- source/api/actions/calendar_get.ts | 2 +- source/api/actions/calendar_list.ts | 2 +- source/api/actions/calendar_remove.ts | 2 +- source/api/actions/events.ts | 2 +- .../{export_caldav.ts => export_ical.ts} | 8 +- source/api/actions/meta_ping.ts | 4 +- source/api/actions/meta_spec.ts | 6 +- source/api/actions/session_begin.ts | 4 +- source/api/actions/session_end.ts | 2 +- source/api/actions/session_oidc.ts | 2 +- source/api/actions/session_prepare.ts | 4 +- source/api/actions/users.ts | 2 +- source/api/base.ts | 18 +- source/api/functions.ts | 11 +- source/api/transformations/datetime.ts | 6 +- source/main.ts | 12 +- tools/makefile | 4 +- tools/update-plankton | 7 +- 28 files changed, 2020 insertions(+), 109 deletions(-) create mode 100644 source/api/actions/caldav_get.ts create mode 100644 source/api/actions/caldav_probe.ts rename source/api/actions/{export_caldav.ts => export_ical.ts} (96%) diff --git a/lib/plankton/plankton.d.ts b/lib/plankton/plankton.d.ts index ade56fc..3b69962 100644 --- a/lib/plankton/plankton.d.ts +++ b/lib/plankton/plankton.d.ts @@ -3348,18 +3348,22 @@ declare namespace lib_plankton.http { patch = "patch" } /** - * @author fenris + * @author roydfalk */ - type type_request = { + type type_request_generic = { scheme: ("http" | "https"); host: (null | string); path: string; version: string; - method: enum_method; + method: type_method; query: (null | string); headers: Record; body: (null | Buffer); }; + /** + * @author fenris + */ + type type_request = type_request_generic; /** * @author fenris */ @@ -3375,10 +3379,22 @@ 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 encode_request(request: type_request): string; + /** + * @author fenris + */ + function has_body(method: enum_method): boolean; + /** + * @author fenris + */ + function decode_request_generic(decode_method_: ((method_raw: string) => type_method), has_body: ((method: type_method) => boolean), request_raw: string): type_request_generic; /** * @author fenris */ @@ -3442,6 +3458,260 @@ declare namespace lib_plankton.http { decode(x: string): type_response; } } +/** + * @author fenris + */ +declare namespace lib_plankton.xml { + /** + * @author fenris + */ + abstract class class_node { + /** + * @author fenris + */ + abstract compile(depth?: int): string; + } + /** + * @author fenris + */ + class class_node_text extends class_node { + /** + * @author fenris + */ + protected content: string; + /** + * @author fenris + */ + constructor(content: string); + /** + * @author fenris + */ + compile(depth?: int): string; + } + /** + * @author fenris + */ + class class_node_comment extends class_node { + /** + * @author fenris + */ + protected content: string; + /** + * @author fenris + */ + constructor(content: string); + /** + * @author fenris + */ + compile(depth?: int): string; + } + /** + * @author fenris + */ + class class_node_complex extends class_node { + /** + * @author fenris + */ + protected name: string; + /** + * @author fenris + */ + protected attributes: { + [key: string]: string; + }; + /** + * @author fenris + */ + protected children: Array; + /** + * @author fenris + */ + constructor(name: string, attributes?: { + [key: string]: string; + }, children?: any[]); + /** + * @author fenris + */ + compile(depth?: int): string; + } +} +declare namespace lib_plankton.webdav { + /** + * @author roydfalk + */ + enum enum_method { + options = "options", + head = "head", + get = "get", + delete = "delete", + post = "post", + put = "put", + patch = "patch", + propfind = "propfind", + proppatch = "proppatch", + mkcol = "mkcol", + copy = "copy", + move = "move", + lock = "lock", + unlock = "unlock" + } + /** + * @author roydfalk + * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_href + */ + type type_data_href = string; + /** + * @author roydfalk + * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_status + */ + type type_data_status = string; + /** + * @author roydfalk + * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_prop + */ + type type_data_prop = { + name: string; + value: (null | string); + }; + /** + * @author roydfalk + * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_propstat + */ + type type_data_propstat = { + prop: Array; + status: string; + description: (null | string); + }; + /** + * @author roydfalk + * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_response + */ + type type_data_response = { + href: type_data_href; + body: ({ + hrefs: Array; + status: string; + } | { + propstats: Array; + }); + description: (null | string); + }; + /** + * @author roydfalk + * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_multistatus + */ + type type_data_multistatus = { + responses: Array; + description: (null | string); + }; + /** + * @author roydfalk + */ + type type_request = lib_plankton.http.type_request_generic; + /** + * @author roydfalk + */ + type type_response = { + version: (null | string); + status_code: int; + headers: Record; + body: Buffer; + }; +} +declare namespace lib_plankton.webdav { + /** + * @author roydfalk + */ + function is_special_method(method: enum_method): boolean; + /** + * @author roydfalk + */ + function encode_method(method: enum_method): string; + /** + * @author roydfalk + */ + function decode_method(method_raw: string): enum_method; + /** + */ + function data_multistatus_encode(data_multistatus: type_data_multistatus): string; + /** + * @author roydfalk + */ + function has_body(method: enum_method): boolean; + /** + * @author fenris + */ + function decode_request(request_raw: string): type_request; + /** + * @author fenris + * @todo try to base on lib_plankton.http.encode_response + */ + function encode_response(response: type_response): string; +} +declare namespace lib_plankton.caldav { + /** + * @author roydfalk + */ + enum enum_method { + options = "options", + head = "head", + get = "get", + delete = "delete", + post = "post", + put = "put", + patch = "patch", + propfind = "propfind", + proppatch = "proppatch", + mkcol = "mkcol", + copy = "copy", + move = "move", + lock = "lock", + unlock = "unlock", + report = "report", + mkcalendar = "mkcalendar", + acl = "acl" + } + /** + * @author roydfalk + */ + type type_request = lib_plankton.http.type_request_generic; + /** + * @author roydfalk + */ + type type_response = { + version: (null | string); + status_code: int; + headers: Record; + body: Buffer; + }; +} +declare namespace lib_plankton.caldav { + /** + * @author roydfalk + */ + function is_special_method(method: enum_method): boolean; + /** + * @author roydfalk + */ + function encode_method(method: enum_method): string; + /** + * @author roydfalk + */ + function decode_method(method_raw: string): enum_method; + /** + * @author roydfalk + */ + function has_body(method: enum_method): boolean; + /** + * @author fenris + */ + function decode_request(request_raw: string): type_request; + /** + * @author fenris + * @todo try to base on lib_plankton.http.encode_response + */ + function encode_response(response: type_response): string; +} declare namespace lib_plankton.markdown { /** * @author fenris @@ -3586,7 +3856,7 @@ declare namespace lib_plankton.api { generate_documentation(): string; } } -declare namespace lib_plankton.rest { +declare namespace lib_plankton.rest_base { /** */ type type_oas_schema = ({} | { @@ -3689,7 +3959,110 @@ declare namespace lib_plankton.rest { }); }; } -declare namespace lib_plankton.rest { +declare namespace lib_plankton.rest_base { + /** + */ + function make(encode_http_method: ((http_method: type_http_method) => string), options?: { + 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"; + parameters: {}; + } | { + kind: "key_header"; + parameters: { + name: string; + }; + }); + actions?: Array<{ + http_method: type_http_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); + }; + }>; + }): type_rest; + /** + */ + function register(encode_http_method: ((http_method: type_http_method) => string), rest: type_rest, http_method: type_http_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; + /** + * @todo check request body mimetype? + * @todo check query paramater validity + */ + function call>(encode_http_method: ((http_method: type_http_method) => string), rest: type_rest, http_request: type_http_request, options?: { + checklevel_restriction?: lib_plankton.api.enum_checklevel; + checklevel_input?: lib_plankton.api.enum_checklevel; + checklevel_output?: lib_plankton.api.enum_checklevel; + }): Promise; + /** + * @see https://swagger.io/specification/#openrest-object + */ + function to_oas(http_request_method_to_oas: ((http_request_method: type_method) => string), rest: type_rest, options?: { + 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?: { @@ -3772,6 +4145,212 @@ declare namespace lib_plankton.rest { servers?: Array; }): any; } +declare namespace lib_plankton.rest_webdav { + /** + */ + 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_webdav { + /** + */ + function make(options?: { + 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"; + parameters: {}; + } | { + kind: "key_header"; + parameters: { + name: string; + }; + }); + actions?: Array<{ + http_method: lib_plankton.webdav.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); + }; + }>; + }): type_rest; + /** + */ + function register(rest: type_rest, http_method: lib_plankton.webdav.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; + /** + * @todo check request body mimetype? + * @todo check query paramater validity + */ + function call(rest: type_rest, http_request: lib_plankton.webdav.type_request, options?: { + checklevel_restriction?: lib_plankton.api.enum_checklevel; + checklevel_input?: lib_plankton.api.enum_checklevel; + checklevel_output?: lib_plankton.api.enum_checklevel; + }): Promise; + /** + * @see https://swagger.io/specification/#openrest-object + */ + function to_oas(rest: type_rest, options?: { + version?: (null | string); + servers?: Array; + }): any; +} +declare namespace lib_plankton.rest_caldav { + /** + */ + 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_caldav { + /** + */ + function make(options?: { + 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"; + parameters: {}; + } | { + kind: "key_header"; + parameters: { + name: string; + }; + }); + actions?: Array<{ + http_method: lib_plankton.caldav.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); + }; + }>; + }): type_rest; + /** + */ + function register(rest: type_rest, http_method: lib_plankton.caldav.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; + /** + * @todo check request body mimetype? + * @todo check query paramater validity + */ + function call(rest: type_rest, http_request: lib_plankton.caldav.type_request, options?: { + checklevel_restriction?: lib_plankton.api.enum_checklevel; + checklevel_input?: lib_plankton.api.enum_checklevel; + checklevel_output?: lib_plankton.api.enum_checklevel; + }): Promise; + /** + * @see https://swagger.io/specification/#openrest-object + */ + function to_oas(rest: type_rest, options?: { + version?: (null | string); + servers?: Array; + }): any; +} declare namespace lib_plankton.server { /** * @author fenris @@ -4359,3 +4938,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 3ccab96..cf79164 100644 --- a/lib/plankton/plankton.js +++ b/lib/plankton/plankton.js @@ -11855,6 +11855,7 @@ var lib_plankton; default: throw (new Error("unhandled method: " + method_raw)); } } + http.decode_method = decode_method; /** * @author fenris */ @@ -11957,11 +11958,22 @@ var lib_plankton; /** * @author fenris */ - function decode_request(request_raw) { + 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 decode_request_generic(decode_method_, has_body, request_raw) { const lines = request_raw.split(linebreak); const first = lines.shift(); const parts = first.split(" "); - const method = decode_method(parts[0]); + const method = decode_method_(parts[0]); const path_and_query = parts[1]; const parts_ = path_and_query.split("?"); const path = parts_[0]; @@ -11978,11 +11990,11 @@ var lib_plankton; headers[key.toLowerCase()] = value; } } - const body = ([http.enum_method.post, http.enum_method.put, http.enum_method.patch].includes(method) + const body = (has_body(method) // @ts-ignore ? Buffer.from(lines.join(linebreak)) : null); - const request = { + const request_generic = { // TODO "scheme": "http", "host": (headers["host"] ?? null), @@ -11993,7 +12005,14 @@ var lib_plankton; "headers": headers, "body": body, }; - return request; + return request_generic; + } + http.decode_request_generic = decode_request_generic; + /** + * @author fenris + */ + function decode_request(request_raw) { + return decode_request_generic(decode_method, has_body, request_raw); } http.decode_request = decode_request; /** @@ -12292,6 +12311,740 @@ var lib_plankton; http.class_http_response = class_http_response; })(http = lib_plankton.http || (lib_plankton.http = {})); })(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:xml«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:xml« 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:xml« 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:xml«. If not, see . + */ +/** + * @author fenris + */ +var lib_plankton; +(function (lib_plankton) { + var xml; + (function (xml) { + /** + * @author fenris + */ + function string_repeat(symbol, count) { + return ((count <= 0) ? "" : (string_repeat(symbol, count - 1) + symbol)); + } + /** + * @author fenris + */ + var class_node = /** @class */ (function () { + function class_node() { + } + return class_node; + }()); + xml.class_node = class_node; + /** + * @author fenris + */ + var class_node_text = /** @class */ (function (_super) { + __extends(class_node_text, _super); + /** + * @author fenris + */ + function class_node_text(content) { + var _this = _super.call(this) || this; + _this.content = content; + return _this; + } + /** + * @author fenris + */ + class_node_text.prototype.compile = function (depth) { + if (depth === void 0) { depth = 0; } + return (string_repeat("\t", depth) + this.content + "\n"); + }; + return class_node_text; + }(class_node)); + xml.class_node_text = class_node_text; + /** + * @author fenris + */ + var class_node_comment = /** @class */ (function (_super) { + __extends(class_node_comment, _super); + /** + * @author fenris + */ + function class_node_comment(content) { + var _this = _super.call(this) || this; + _this.content = content; + return _this; + } + /** + * @author fenris + */ + class_node_comment.prototype.compile = function (depth) { + if (depth === void 0) { depth = 0; } + return (string_repeat("\t", depth) + "" + "\n"); + }; + return class_node_comment; + }(class_node)); + xml.class_node_comment = class_node_comment; + /** + * @author fenris + */ + var class_node_complex = /** @class */ (function (_super) { + __extends(class_node_complex, _super); + /** + * @author fenris + */ + function class_node_complex(name, attributes, children) { + if (attributes === void 0) { attributes = {}; } + if (children === void 0) { children = []; } + var _this = _super.call(this) || this; + _this.name = name; + _this.attributes = attributes; + _this.children = children; + return _this; + } + /** + * @author fenris + */ + class_node_complex.prototype.compile = function (depth) { + var _this = this; + if (depth === void 0) { depth = 0; } + var output = ""; + var attributes = (Object.keys(this.attributes) + .filter(function (key) { return (_this.attributes[key] !== null); }) + .map(function (key) { return (" " + key + "=" + ("\"" + _this.attributes[key] + "\"")); }) + .join("")); + output += (string_repeat("\t", depth) + "<" + this.name + attributes + ">" + "\n"); + this.children.forEach(function (child) { return (output += child.compile(depth + 1)); }); + output += (string_repeat("\t", depth) + "" + "\n"); + return output; + }; + return class_node_complex; + }(class_node)); + xml.class_node_complex = class_node_complex; + })(xml = lib_plankton.xml || (lib_plankton.xml = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:webdav«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:webdav« 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:webdav« 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:webdav«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var webdav; + (function (webdav) { + /** + * @author roydfalk + */ + let enum_method; + (function (enum_method) { + enum_method["options"] = "options"; + enum_method["head"] = "head"; + enum_method["get"] = "get"; + enum_method["delete"] = "delete"; + enum_method["post"] = "post"; + enum_method["put"] = "put"; + enum_method["patch"] = "patch"; + enum_method["propfind"] = "propfind"; + enum_method["proppatch"] = "proppatch"; + enum_method["mkcol"] = "mkcol"; + enum_method["copy"] = "copy"; + enum_method["move"] = "move"; + enum_method["lock"] = "lock"; + enum_method["unlock"] = "unlock"; + })(enum_method = webdav.enum_method || (webdav.enum_method = {})); + ; + })(webdav = lib_plankton.webdav || (lib_plankton.webdav = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:webdav«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:webdav« 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:webdav« 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:webdav«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var webdav; + (function (webdav) { + /** + * @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 roydfalk + */ + function is_special_method(method) { + return ((method === webdav.enum_method.propfind) + || + (method === webdav.enum_method.proppatch) + || + (method === webdav.enum_method.mkcol) + || + (method === webdav.enum_method.copy) + || + (method === webdav.enum_method.move) + || + (method === webdav.enum_method.lock) + || + (method === webdav.enum_method.unlock)); + } + webdav.is_special_method = is_special_method; + /** + * @author roydfalk + */ + function encode_method(method) { + switch (method) { + case webdav.enum_method.get: return "GET"; + case webdav.enum_method.post: return "POST"; + case webdav.enum_method.patch: return "PATCH"; + case webdav.enum_method.put: return "PUT"; + case webdav.enum_method.delete: return "DELETE"; + case webdav.enum_method.options: return "OPTIONS"; + case webdav.enum_method.head: return "HEAD"; + case webdav.enum_method.propfind: return "PROPFIND"; + case webdav.enum_method.proppatch: return "PROPPATCH"; + case webdav.enum_method.mkcol: return "MKCOL"; + case webdav.enum_method.copy: return "COPY"; + case webdav.enum_method.move: return "MOVE"; + case webdav.enum_method.lock: return "LOCK"; + case webdav.enum_method.unlock: return "UNLOCK"; + default: throw (new Error("impossible")); + } + } + webdav.encode_method = encode_method; + /** + * @author roydfalk + */ + function decode_method(method_raw) { + switch (method_raw) { + case "GET": return webdav.enum_method.get; + case "POST": return webdav.enum_method.post; + case "PATCH": return webdav.enum_method.patch; + case "PUT": return webdav.enum_method.put; + case "DELETE": return webdav.enum_method.delete; + case "OPTIONS": return webdav.enum_method.options; + case "HEAD": return webdav.enum_method.head; + case "PROPFIND": return webdav.enum_method.propfind; + case "PROPPATCH": return webdav.enum_method.proppatch; + case "MKCOL": return webdav.enum_method.mkcol; + case "COPY": return webdav.enum_method.copy; + case "MOVE": return webdav.enum_method.move; + case "LOCK": return webdav.enum_method.lock; + case "UNLOCK": return webdav.enum_method.unlock; + default: throw (new Error("unhandled method: " + method_raw)); + } + } + webdav.decode_method = decode_method; + /** + * @author fenris + */ + function get_statustext(statuscode) { + switch (statuscode) { + case 100: return "Continue"; + case 101: return "Switching Protocols"; + case 103: return "Early Hints"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 207: return "Multistatus"; + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 307: return "Temporary Redirect"; + case 308: return "Permanent Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Payload Too Large"; + case 414: return "URI Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 418: return "I'm a teapot"; + case 422: return "Unprocessable Entity"; + case 425: return "Too Early"; + case 426: return "Upgrade Required"; + case 428: return "Precondition Required"; + case 429: return "Too Many Requests"; + case 431: return "Request Header Fields Too Large"; + case 451: return "Unavailable For Legal Reasons"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "HTTP Version Not Supported"; + case 506: return "Variant Also Negotiates"; + case 507: return "Insufficient Storage"; + case 508: return "Loop Detected"; + case 510: return "Not Extended"; + case 511: return "Network Authentication"; + default: throw (new Error("unhandled statuscode: " + statuscode.toFixed(0))); + } + } + /** + */ + function data_href_encode_xml(data_href) { + return (new lib_plankton.xml.class_node_complex("d:href", {}, [ + new lib_plankton.xml.class_node_text(data_href), + ])); + } + /** + */ + function data_status_encode_xml(data_status) { + return (new lib_plankton.xml.class_node_complex("d:status", {}, [ + new lib_plankton.xml.class_node_text(data_status), + ])); + } + /** + */ + function data_prop_encode_xml(data_prop) { + return (new lib_plankton.xml.class_node_complex(data_prop.name, {}, [ + new lib_plankton.xml.class_node_text(data_prop.value ?? ""), + ])); + } + /** + */ + function data_propstat_encode_xml(data_propstat) { + return (new lib_plankton.xml.class_node_complex("d:propstat", { + // todo xmlns:R + }, [ + new lib_plankton.xml.class_node_complex("d:prop", { + // todo xmlns:R + }, data_propstat.prop.map(data_prop_encode_xml)), + data_status_encode_xml(data_propstat.status), + ])); + } + /** + */ + function data_response_encode_xml(data_response) { + return (new lib_plankton.xml.class_node_complex("d:response", {}, ([ + data_href_encode_xml(data_response.href), + ] + .concat(("hrefs" in data_response.body) + ? + (data_response.body.hrefs.map(data_href_encode_xml) + .concat([ + data_status_encode_xml(data_response.body.status), + ])) + : + data_response.body.propstats.map(data_propstat_encode_xml))))); + } + /** + */ + function data_multistatus_encode_xml(data_multistatus) { + return (new lib_plankton.xml.class_node_complex("d:multistatus", { + "xmlns:d": "DAV:", + }, (data_multistatus.responses.map(data_response_encode_xml) + .concat()))); + } + /** + */ + function data_multistatus_encode(data_multistatus) { + return data_multistatus_encode_xml(data_multistatus).compile(); + } + webdav.data_multistatus_encode = data_multistatus_encode; + /** + * @author roydfalk + */ + function has_body(method) { + return [ + webdav.enum_method.post, + webdav.enum_method.put, + webdav.enum_method.patch, + webdav.enum_method.propfind, + webdav.enum_method.proppatch, + webdav.enum_method.mkcol, + webdav.enum_method.copy, + webdav.enum_method.move, + webdav.enum_method.lock, + webdav.enum_method.unlock, // TODO verify + ].includes(method); + } + webdav.has_body = has_body; + /** + * @author fenris + */ + function decode_request(request_raw) { + return lib_plankton.http.decode_request_generic(decode_method, has_body, request_raw); + } + webdav.decode_request = decode_request; + /** + * @author fenris + * @todo try to base on lib_plankton.http.encode_response + */ + 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; + } + webdav.encode_response = encode_response; + })(webdav = lib_plankton.webdav || (lib_plankton.webdav = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:caldav«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:caldav« 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:caldav« 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:caldav«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var caldav; + (function (caldav) { + /** + * @author roydfalk + */ + let enum_method; + (function (enum_method) { + enum_method["options"] = "options"; + enum_method["head"] = "head"; + enum_method["get"] = "get"; + enum_method["delete"] = "delete"; + enum_method["post"] = "post"; + enum_method["put"] = "put"; + enum_method["patch"] = "patch"; + enum_method["propfind"] = "propfind"; + enum_method["proppatch"] = "proppatch"; + enum_method["mkcol"] = "mkcol"; + enum_method["copy"] = "copy"; + enum_method["move"] = "move"; + enum_method["lock"] = "lock"; + enum_method["unlock"] = "unlock"; + enum_method["report"] = "report"; + enum_method["mkcalendar"] = "mkcalendar"; + enum_method["acl"] = "acl"; + })(enum_method = caldav.enum_method || (caldav.enum_method = {})); + ; + })(caldav = lib_plankton.caldav || (lib_plankton.caldav = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:caldav«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:caldav« 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:caldav« 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:caldav«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var caldav; + (function (caldav) { + /** + * @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 roydfalk + */ + function is_special_method(method) { + return ((method === caldav.enum_method.report) + || + (method === caldav.enum_method.mkcalendar) + || + (method === caldav.enum_method.acl)); + } + caldav.is_special_method = is_special_method; + /** + * @author roydfalk + */ + function encode_method(method) { + switch (method) { + case caldav.enum_method.get: return "GET"; + case caldav.enum_method.post: return "POST"; + case caldav.enum_method.patch: return "PATCH"; + case caldav.enum_method.put: return "PUT"; + case caldav.enum_method.delete: return "DELETE"; + case caldav.enum_method.options: return "OPTIONS"; + case caldav.enum_method.head: return "HEAD"; + case caldav.enum_method.propfind: return "PROPFIND"; + case caldav.enum_method.proppatch: return "PROPPATCH"; + case caldav.enum_method.mkcol: return "MKCOL"; + case caldav.enum_method.copy: return "COPY"; + case caldav.enum_method.move: return "MOVE"; + case caldav.enum_method.lock: return "LOCK"; + case caldav.enum_method.unlock: return "UNLOCK"; + case caldav.enum_method.report: return "REPORT"; + case caldav.enum_method.mkcalendar: return "MKCALENDAR"; + case caldav.enum_method.acl: return "ACL"; + default: throw (new Error("impossible")); + } + } + caldav.encode_method = encode_method; + /** + * @author roydfalk + */ + function decode_method(method_raw) { + switch (method_raw) { + case "GET": return caldav.enum_method.get; + case "POST": return caldav.enum_method.post; + case "PATCH": return caldav.enum_method.patch; + case "PUT": return caldav.enum_method.put; + case "DELETE": return caldav.enum_method.delete; + case "OPTIONS": return caldav.enum_method.options; + case "HEAD": return caldav.enum_method.head; + case "PROPFIND": return caldav.enum_method.propfind; + case "PROPPATCH": return caldav.enum_method.proppatch; + case "MKCOL": return caldav.enum_method.mkcol; + case "COPY": return caldav.enum_method.copy; + case "MOVE": return caldav.enum_method.move; + case "LOCK": return caldav.enum_method.lock; + case "UNLOCK": return caldav.enum_method.unlock; + case "REPORT": return caldav.enum_method.report; + case "MKCALENDAR": return caldav.enum_method.mkcalendar; + case "ACL": return caldav.enum_method.acl; + default: throw (new Error("unhandled method: " + method_raw)); + } + } + caldav.decode_method = decode_method; + /** + * @author fenris + */ + function get_statustext(statuscode) { + switch (statuscode) { + case 100: return "Continue"; + case 101: return "Switching Protocols"; + case 103: return "Early Hints"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 207: return "Multistatus"; + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 307: return "Temporary Redirect"; + case 308: return "Permanent Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Payload Too Large"; + case 414: return "URI Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 418: return "I'm a teapot"; + case 422: return "Unprocessable Entity"; + case 425: return "Too Early"; + case 426: return "Upgrade Required"; + case 428: return "Precondition Required"; + case 429: return "Too Many Requests"; + case 431: return "Request Header Fields Too Large"; + case 451: return "Unavailable For Legal Reasons"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "HTTP Version Not Supported"; + case 506: return "Variant Also Negotiates"; + case 507: return "Insufficient Storage"; + case 508: return "Loop Detected"; + case 510: return "Not Extended"; + case 511: return "Network Authentication"; + default: throw (new Error("unhandled statuscode: " + statuscode.toFixed(0))); + } + } + /** + * @author roydfalk + */ + function has_body(method) { + return [ + caldav.enum_method.post, + caldav.enum_method.put, + caldav.enum_method.patch, + caldav.enum_method.propfind, + caldav.enum_method.proppatch, + caldav.enum_method.mkcol, + caldav.enum_method.copy, + caldav.enum_method.move, + caldav.enum_method.lock, + caldav.enum_method.unlock, + caldav.enum_method.mkcalendar, // TODO verify + ].includes(method); + } + caldav.has_body = has_body; + /** + * @author fenris + */ + function decode_request(request_raw) { + return lib_plankton.http.decode_request_generic(decode_method, has_body, request_raw); + } + caldav.decode_request = decode_request; + /** + * @author fenris + * @todo try to base on lib_plankton.http.encode_response + */ + 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; + } + caldav.encode_response = encode_response; + })(caldav = lib_plankton.caldav || (lib_plankton.caldav = {})); +})(lib_plankton || (lib_plankton = {})); /* This file is part of »bacterio-plankton:markdown«. @@ -12702,61 +13455,47 @@ var lib_plankton; })(api = lib_plankton.api || (lib_plankton.api = {})); })(lib_plankton || (lib_plankton = {})); /* -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) { @@ -12795,11 +13534,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, }; @@ -12807,7 +13546,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 @@ -12884,12 +13623,13 @@ 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 { @@ -12898,7 +13638,7 @@ var lib_plankton; "steps": steps, }); } - routenode.operations[http_method] = operation; + routenode.operations[http_method_encoded] = operation; } else { const steps_head = steps[0]; @@ -12913,7 +13653,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), }; } } @@ -12931,14 +13671,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 @@ -12946,7 +13686,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); } } } @@ -12954,7 +13694,7 @@ var lib_plankton; } /** */ - function make(options = {}) { + function make(encode_http_method, options = {}) { options = lib_plankton.object.patched({ "title": "REST-API", "versioning_method": "none", @@ -12982,15 +13722,15 @@ var lib_plankton; "set_access_control_headers": options.set_access_control_headers, "authentication": options.authentication, }; - options.actions.forEach(action_definition => { - rest.register(subject, action_definition.http_method, action_definition.path, action_definition.options); + options.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) { + function register(encode_http_method, rest, http_method, path, options) { options = lib_plankton.object.patched({ "active": ((version) => true), "execution": ((stuff) => Promise.resolve({ "status_code": 501, "data": null })), @@ -13020,7 +13760,8 @@ var lib_plankton; const steps_enriched = ((rest.versioning_method === "path") ? ["{version}"].concat(steps) : steps); - const action_name = (steps.concat([lib_plankton.http.encode_method(http_method).toLowerCase()]) + 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, @@ -13032,7 +13773,7 @@ var lib_plankton; "input_schema": options.input_schema, "output_schema": options.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, { @@ -13062,12 +13803,12 @@ var lib_plankton; // "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 = {}) { + async function call(encode_http_method, 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, @@ -13101,7 +13842,6 @@ 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)) .join(", ")); // get version let version; @@ -13165,7 +13905,8 @@ var lib_plankton; }; } else { - if (!(http_request.method in stuff.routenode.operations)) { + const http_method_encoded = encode_http_method(http_request.method).toLowerCase(); + if (!(http_method_encoded in stuff.routenode.operations)) { if (Object.keys(stuff.routenode.operations).length <= 0) { return { "version": "HTTP/1.1", @@ -13189,7 +13930,7 @@ var lib_plankton; // 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, @@ -13314,11 +14055,11 @@ var lib_plankton; } } } - rest_1.call = call; + rest_base.call = call; /** * @see https://swagger.io/specification/#openrest-object */ - function to_oas(rest, options = {}) { + function to_oas(http_request_method_to_oas, rest, options = {}) { options = lib_plankton.object.patched({ "version": null, "servers": [], @@ -13453,8 +14194,280 @@ var lib_plankton; ]) }; } - 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 + */ + async function call(rest, http_request, options = {}) { + return lib_plankton.rest_base.call(lib_plankton.http.encode_method, rest, http_request, options); + } + 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, 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:rest_webdav«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:rest_webdav« 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_webdav« 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_webdav«. If not, see . + */ +/* +This file is part of »bacterio-plankton:rest_webdav«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:rest_webdav« 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_webdav« 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_webdav«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var rest_webdav; + (function (rest_webdav) { + /** + */ + function http_request_method_to_oas(http_request_method) { + switch (http_request_method) { + case lib_plankton.webdav.enum_method.get: return "get"; + case lib_plankton.webdav.enum_method.post: return "post"; + case lib_plankton.webdav.enum_method.patch: return "patch"; + case lib_plankton.webdav.enum_method.head: return "head"; + case lib_plankton.webdav.enum_method.delete: return "delete"; + case lib_plankton.webdav.enum_method.options: return "options"; + case lib_plankton.webdav.enum_method.put: return "put"; + case lib_plankton.webdav.enum_method.propfind: return "PROPFIND"; + case lib_plankton.webdav.enum_method.proppatch: return "PROPPATCH"; + case lib_plankton.webdav.enum_method.mkcol: return "MKCOL"; + case lib_plankton.webdav.enum_method.copy: return "COPY"; + case lib_plankton.webdav.enum_method.move: return "MOVE"; + case lib_plankton.webdav.enum_method.lock: return "LOCK"; + case lib_plankton.webdav.enum_method.unlock: return "UNLOCK"; + default: throw (new Error("impossible")); + } + } + /** + */ + function make(options = {}) { + return lib_plankton.rest_base.make(lib_plankton.webdav.encode_method, options); + } + rest_webdav.make = make; + /** + */ + function register(rest, http_method, path, options) { + lib_plankton.rest_base.register(lib_plankton.webdav.encode_method, rest, http_method, path, options); + } + rest_webdav.register = register; + /** + * @todo check request body mimetype? + * @todo check query paramater validity + */ + async function call(rest, http_request, options = {}) { + return lib_plankton.rest_base.call(lib_plankton.webdav.encode_method, rest, http_request, options); + } + rest_webdav.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, rest, options); + } + rest_webdav.to_oas = to_oas; + })(rest_webdav = lib_plankton.rest_webdav || (lib_plankton.rest_webdav = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:rest_caldav«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:rest_caldav« 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_caldav« 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_caldav«. If not, see . + */ +/* +This file is part of »bacterio-plankton:rest_caldav«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:rest_caldav« 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_caldav« 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_caldav«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var rest_caldav; + (function (rest_caldav) { + /** + */ + function http_request_method_to_oas(http_request_method) { + switch (http_request_method) { + case lib_plankton.caldav.enum_method.get: return "get"; + case lib_plankton.caldav.enum_method.post: return "post"; + case lib_plankton.caldav.enum_method.patch: return "patch"; + case lib_plankton.caldav.enum_method.head: return "head"; + case lib_plankton.caldav.enum_method.delete: return "delete"; + case lib_plankton.caldav.enum_method.options: return "options"; + case lib_plankton.caldav.enum_method.put: return "put"; + case lib_plankton.caldav.enum_method.propfind: return "PROPFIND"; + case lib_plankton.caldav.enum_method.proppatch: return "PROPPATCH"; + case lib_plankton.caldav.enum_method.mkcol: return "MKCOL"; + case lib_plankton.caldav.enum_method.copy: return "COPY"; + case lib_plankton.caldav.enum_method.move: return "MOVE"; + case lib_plankton.caldav.enum_method.lock: return "LOCK"; + case lib_plankton.caldav.enum_method.unlock: return "UNLOCK"; + case lib_plankton.caldav.enum_method.report: return "REPORT"; + case lib_plankton.caldav.enum_method.mkcalendar: return "MKCALENDAR"; + case lib_plankton.caldav.enum_method.acl: return "ACL"; + default: throw (new Error("impossible")); + } + } + /** + */ + function make(options = {}) { + return lib_plankton.rest_base.make(lib_plankton.caldav.encode_method, options); + } + rest_caldav.make = make; + /** + */ + function register(rest, http_method, path, options) { + lib_plankton.rest_base.register(lib_plankton.caldav.encode_method, rest, http_method, path, options); + } + rest_caldav.register = register; + /** + * @todo check request body mimetype? + * @todo check query paramater validity + */ + async function call(rest, http_request, options = {}) { + return lib_plankton.rest_base.call(lib_plankton.caldav.encode_method, rest, http_request, options); + } + rest_caldav.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, rest, options); + } + rest_caldav.to_oas = to_oas; + })(rest_caldav = lib_plankton.rest_caldav || (lib_plankton.rest_caldav = {})); })(lib_plankton || (lib_plankton = {})); /* This file is part of »bacterio-plankton:server«. @@ -15472,3 +16485,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/source/api/actions/caldav_get.ts b/source/api/actions/caldav_get.ts new file mode 100644 index 0000000..8812e8a --- /dev/null +++ b/source/api/actions/caldav_get.ts @@ -0,0 +1,166 @@ + +namespace _zeitbild.api +{ + + /** + */ + export function register_caldav_get( + rest_subject : lib_plankton.rest_caldav.type_rest + ) : void + { + register< + null, + ( + lib_plankton.ical.type_vcalendar + | + string + ) + >( + rest_subject, + lib_plankton.caldav.enum_method.report, + "/caldav", + { + "description": "trägt Veranstaltungen aus verschiedenen Kalendern zusammen im ical-Format", + "query_parameters": () => ([ + { + "name": "from", + "required": false, + "description": "UNIX timestamp", + }, + { + "name": "to", + "required": false, + "description": "UNIX timestamp", + }, + { + "name": "calendar_ids", + "required": false, + "description": "comma separated", + }, + { + "name": "auth", + "required": true, + "description": "", + }, + ]), + "output_schema": () => ({ + "nullable": false, + "type": "string", + }), + "response_body_mimetype": "text/calendar", + "response_body_encode": (output) => Buffer.from( + (typeof(output) === "string") + ? + output + : + lib_plankton.ical.ics_encode(output) + ), + "restriction": restriction_none, + "execution": async (stuff) => { + const user_id : (null | _zeitbild.type_user_id) = await ( + session_from_stuff(stuff) + .then( + (session : {key : string; value : lib_plankton.session.type_session;}) => ( + _zeitbild.service.user.identify(session.value.name) + .catch(x => Promise.resolve(null)) + ) + ) + .catch(x => Promise.resolve(null)) + ); + + const from : lib_plankton.pit.type_pit = ( + ("from" in stuff.query_parameters) + ? + parseInt(stuff.query_parameters["from"]) + : + lib_plankton.pit.shift_week( + lib_plankton.pit.now(), + -2 + ) + ); + const to : lib_plankton.pit.type_pit = ( + ("to" in stuff.query_parameters) + ? + parseInt(stuff.query_parameters["to"]) + : + lib_plankton.pit.shift_week( + lib_plankton.pit.now(), + +6 + ) + ); + const calendar_ids_wanted : (null | Array<_zeitbild.type_calendar_id>) = ( + ( + ("calendar_ids" in stuff.query_parameters) + && + (stuff.query_parameters["calendar_ids"] !== null) + ) + ? + lib_plankton.call.convey( + stuff.query_parameters["calendar_ids"], + [ + (x : string) => x.split(","), + (x : Array) => x.map(parseInt), + (x : Array) => x.filter(y => (! isNaN(y))) + ] + ) + : + null + ); + + const auth_hash_shall : string = lib_plankton.sha256.get( + (stuff.query_parameters["calendar_ids"] ?? ""), + _zeitbild.conf.get()["misc"]["auth_salt"] + ); + const auth_hash_is : string = stuff.query_parameters["auth"]; + /** + * @todo remove + */ + lib_plankton.log.info( + "auth_hashes", + { + "shall": auth_hash_shall, + "is": auth_hash_is, + } + ); + if (! (auth_hash_is === auth_hash_shall)) { + return Promise.resolve( + { + "status_code": 403, + "data": "not authorized", + } + ); + } + else { + return ( + _zeitbild.service.calendar.gather_events( + calendar_ids_wanted, + from, + to, + user_id + ) + .then( + (data) => Promise.resolve( + { + "status_code": 200, + "data": _zeitbild.helpers.ical_vcalendar_from_own_event_list( + data.map(entry => entry.event_object) + ) + } + ) + ) + .catch( + (reason) => Promise.resolve( + { + "status_code": 403, + "data": String(reason), + } + ) + ) + ); + } + } + } + ); + } + +} diff --git a/source/api/actions/caldav_probe.ts b/source/api/actions/caldav_probe.ts new file mode 100644 index 0000000..58b80e2 --- /dev/null +++ b/source/api/actions/caldav_probe.ts @@ -0,0 +1,100 @@ + +namespace _zeitbild.api +{ + + /** + */ + export function register_caldav_probe( + rest_subject : lib_plankton.rest_caldav.type_rest + ) : void + { + register< + any, + any + >( + rest_subject, + lib_plankton.caldav.enum_method.propfind, + "/caldav", + { + "query_parameters": () => ([ + { + "name": "from", + "required": false, + "description": "UNIX timestamp", + }, + { + "name": "to", + "required": false, + "description": "UNIX timestamp", + }, + { + "name": "calendar_ids", + "required": false, + "description": "comma separated", + }, + { + "name": "auth", + "required": true, + "description": "", + }, + ]), + "output_schema": () => ({ + "nullable": false, + "type": "string", + }), + "response_body_mimetype": "application/xml", + "response_body_encode": output => Buffer.from(output), + "restriction": restriction_none, + /** + * @todo examine body + */ + "execution": async (stuff) => { + return Promise.resolve( + { + "status_code": 207, + "data": ( + /*"\n" + +*/ + lib_plankton.webdav.data_multistatus_encode( + { + "responses": [ + { + "href": "/caldav/events", + "body": { + "propstats": [ + { + "prop": [ + {"name": "d:displayname", "value": "default"}, + // {"name": "cs:getctag", "value": "47"}, // TODO correct value + // {"name": "current-user-privilege-set", "value": ""}, + /* + "uid", + "dtstamp", + "dtstart", + "dtend", + "summary", + "description", + "url", + "location", + */ + ], + "status": "HTTP/2.0 200 OK", + "description": null, + }, + ] + }, + "description": null, + } + ], + "description": null, + } + ) + ), + } + ); + } + } + ); + } + +} diff --git a/source/api/actions/calendar_add.ts b/source/api/actions/calendar_add.ts index 949cbdc..b2b87d2 100644 --- a/source/api/actions/calendar_add.ts +++ b/source/api/actions/calendar_add.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_calendar_add( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/actions/calendar_change.ts b/source/api/actions/calendar_change.ts index 07a32d8..6ce095e 100644 --- a/source/api/actions/calendar_change.ts +++ b/source/api/actions/calendar_change.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_calendar_change( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/actions/calendar_event_add.ts b/source/api/actions/calendar_event_add.ts index df4c00e..b7a4b47 100644 --- a/source/api/actions/calendar_event_add.ts +++ b/source/api/actions/calendar_event_add.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_calendar_event_add( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/actions/calendar_event_change.ts b/source/api/actions/calendar_event_change.ts index 42ec2d3..9fe6963 100644 --- a/source/api/actions/calendar_event_change.ts +++ b/source/api/actions/calendar_event_change.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_calendar_event_change( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/actions/calendar_event_get.ts b/source/api/actions/calendar_event_get.ts index 34427a7..8fb59c2 100644 --- a/source/api/actions/calendar_event_get.ts +++ b/source/api/actions/calendar_event_get.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_calendar_event_get( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/actions/calendar_event_remove.ts b/source/api/actions/calendar_event_remove.ts index cd83b3a..7df847a 100644 --- a/source/api/actions/calendar_event_remove.ts +++ b/source/api/actions/calendar_event_remove.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_calendar_event_remove( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/actions/calendar_get.ts b/source/api/actions/calendar_get.ts index cdb4b0d..a042ab7 100644 --- a/source/api/actions/calendar_get.ts +++ b/source/api/actions/calendar_get.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_calendar_get( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/actions/calendar_list.ts b/source/api/actions/calendar_list.ts index 5c345dd..6770c75 100644 --- a/source/api/actions/calendar_list.ts +++ b/source/api/actions/calendar_list.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_calendar_list( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/actions/calendar_remove.ts b/source/api/actions/calendar_remove.ts index 693afc4..f430477 100644 --- a/source/api/actions/calendar_remove.ts +++ b/source/api/actions/calendar_remove.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_calendar_remove( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/actions/events.ts b/source/api/actions/events.ts index 5feecb1..8be3f78 100644 --- a/source/api/actions/events.ts +++ b/source/api/actions/events.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_events( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/actions/export_caldav.ts b/source/api/actions/export_ical.ts similarity index 96% rename from source/api/actions/export_caldav.ts rename to source/api/actions/export_ical.ts index d970b6e..b8f68d4 100644 --- a/source/api/actions/export_caldav.ts +++ b/source/api/actions/export_ical.ts @@ -4,8 +4,8 @@ namespace _zeitbild.api /** */ - export function register_export_caldav( - rest_subject : lib_plankton.rest.type_rest + export function register_export_ical( + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< @@ -18,9 +18,9 @@ namespace _zeitbild.api >( rest_subject, lib_plankton.http.enum_method.get, - "/export/caldav", + "/export/ical", { - "description": "trägt Veranstaltungen aus verschiedenen Kalendern zusammen im CalDAV-Format", + "description": "trägt Veranstaltungen aus verschiedenen Kalendern zusammen im ical-Format", "query_parameters": () => ([ { "name": "from", diff --git a/source/api/actions/meta_ping.ts b/source/api/actions/meta_ping.ts index bfe01f1..77f8258 100644 --- a/source/api/actions/meta_ping.ts +++ b/source/api/actions/meta_ping.ts @@ -5,10 +5,10 @@ namespace _zeitbild.api /** */ export function register_meta_ping( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { - lib_plankton.rest.register< + lib_plankton.rest_caldav.register< null, string > diff --git a/source/api/actions/meta_spec.ts b/source/api/actions/meta_spec.ts index cecb3ad..5dca37d 100644 --- a/source/api/actions/meta_spec.ts +++ b/source/api/actions/meta_spec.ts @@ -5,10 +5,10 @@ namespace _zeitbild.api /** */ export function register_meta_spec( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { - lib_plankton.rest.register< + lib_plankton.rest_caldav.register< null, any > @@ -27,7 +27,7 @@ namespace _zeitbild.api "execution": () => { return Promise.resolve({ "status_code": 200, - "data": lib_plankton.rest.to_oas(rest_subject), + "data": lib_plankton.rest_caldav.to_oas(rest_subject), }); }, } diff --git a/source/api/actions/session_begin.ts b/source/api/actions/session_begin.ts index 8125575..62ab793 100644 --- a/source/api/actions/session_begin.ts +++ b/source/api/actions/session_begin.ts @@ -5,10 +5,10 @@ namespace _zeitbild.api /** */ export function register_session_begin( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { - lib_plankton.rest.register< + lib_plankton.rest_caldav.register< { name : string; password : string; diff --git a/source/api/actions/session_end.ts b/source/api/actions/session_end.ts index 124bb5e..3ecbba7 100644 --- a/source/api/actions/session_end.ts +++ b/source/api/actions/session_end.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_session_end( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register( diff --git a/source/api/actions/session_oidc.ts b/source/api/actions/session_oidc.ts index d609537..9ace510 100644 --- a/source/api/actions/session_oidc.ts +++ b/source/api/actions/session_oidc.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_session_oidc( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/actions/session_prepare.ts b/source/api/actions/session_prepare.ts index e76e225..8753106 100644 --- a/source/api/actions/session_prepare.ts +++ b/source/api/actions/session_prepare.ts @@ -5,10 +5,10 @@ namespace _zeitbild.api /** */ export function register_session_prepare( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { - lib_plankton.rest.register< + lib_plankton.rest_caldav.register< any, { kind : string; diff --git a/source/api/actions/users.ts b/source/api/actions/users.ts index fc4010b..3f667bc 100644 --- a/source/api/actions/users.ts +++ b/source/api/actions/users.ts @@ -5,7 +5,7 @@ namespace _zeitbild.api /** */ export function register_users( - rest_subject : lib_plankton.rest.type_rest + rest_subject : lib_plankton.rest_caldav.type_rest ) : void { register< diff --git a/source/api/base.ts b/source/api/base.ts index 56f7a07..4f85f52 100644 --- a/source/api/base.ts +++ b/source/api/base.ts @@ -27,7 +27,7 @@ namespace _zeitbild.api /** */ - export const restriction_logged_in : lib_plankton.rest.type_restriction = ( + export const restriction_logged_in : lib_plankton.rest_caldav.type_restriction = ( (stuff) => ( session_from_stuff(stuff) .then(() => Promise.resolve(true)) @@ -38,7 +38,7 @@ namespace _zeitbild.api /** */ - export const restriction_none : lib_plankton.rest.type_restriction = ( + export const restriction_none : lib_plankton.rest_caldav.type_restriction = ( (stuff) => Promise.resolve(true) ); @@ -46,13 +46,13 @@ namespace _zeitbild.api /** */ export function register( - rest_subject : lib_plankton.rest.type_rest, - http_method : lib_plankton.http.enum_method, + rest_subject : lib_plankton.rest_caldav.type_rest, + http_method : lib_plankton.caldav.enum_method, path : string, options : { active ?: ((version : string) => boolean); - restriction ?: (null | lib_plankton.rest.type_restriction); - execution ?: lib_plankton.rest.type_execution; + restriction ?: (null | lib_plankton.rest_caldav.type_restriction); + execution ?: lib_plankton.rest_caldav.type_execution; title ?: (null | string); description ?: (null | string); query_parameters ?: ((version : (null | string)) => Array< @@ -62,8 +62,8 @@ namespace _zeitbild.api required : boolean; } >); - input_schema ?: ((version: (null | string)) => lib_plankton.rest.type_oas_schema); - output_schema ?: ((version: (null | string)) => lib_plankton.rest.type_oas_schema); + input_schema ?: ((version: (null | string)) => lib_plankton.rest_caldav.type_oas_schema); + output_schema ?: ((version: (null | string)) => lib_plankton.rest_caldav.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; @@ -76,7 +76,7 @@ namespace _zeitbild.api }, options ); - lib_plankton.rest.register( + lib_plankton.rest_caldav.register( rest_subject, http_method, (_zeitbild.conf.get().server.path_base + path), diff --git a/source/api/functions.ts b/source/api/functions.ts index 1d24611..8344792 100644 --- a/source/api/functions.ts +++ b/source/api/functions.ts @@ -5,9 +5,9 @@ namespace _zeitbild.api /** */ export function make( - ) : lib_plankton.rest.type_rest + ) : lib_plankton.rest_caldav.type_rest { - const rest_subject : lib_plankton.rest.type_rest = lib_plankton.rest.make( + const rest_subject : lib_plankton.rest_caldav.type_rest = lib_plankton.rest_caldav.make( { "title": "zeitbild", "versioning_method": "header", @@ -48,7 +48,12 @@ namespace _zeitbild.api } // export { - _zeitbild.api.register_export_caldav(rest_subject); + _zeitbild.api.register_export_ical(rest_subject); + } + // caldav + { + _zeitbild.api.register_caldav_probe(rest_subject); + _zeitbild.api.register_caldav_get(rest_subject); } // misc { diff --git a/source/api/transformations/datetime.ts b/source/api/transformations/datetime.ts index bb2a51d..72f515e 100644 --- a/source/api/transformations/datetime.ts +++ b/source/api/transformations/datetime.ts @@ -8,7 +8,7 @@ namespace _zeitbild.api options : { nullable ?: boolean; } = {} - ) : lib_plankton.rest.type_oas_schema + ) : lib_plankton.rest_caldav.type_oas_schema { options = Object.assign( { @@ -49,7 +49,7 @@ namespace _zeitbild.api options : { nullable ?: boolean; } = {} - ) : lib_plankton.rest.type_oas_schema + ) : lib_plankton.rest_caldav.type_oas_schema { options = Object.assign( { @@ -90,7 +90,7 @@ namespace _zeitbild.api options : { nullable ?: boolean; } = {} - ) : lib_plankton.rest.type_oas_schema + ) : lib_plankton.rest_caldav.type_oas_schema { options = Object.assign( { diff --git a/source/main.ts b/source/main.ts index 326b563..858131c 100644 --- a/source/main.ts +++ b/source/main.ts @@ -320,11 +320,11 @@ async function main( } case "api-doc": { lib_plankton.log.conf_push([]); - const rest_subject : lib_plankton.rest.type_rest = _zeitbild.api.make(); + const rest_subject : lib_plankton.rest_caldav.type_rest = _zeitbild.api.make(); lib_plankton.log.conf_pop(); process.stdout.write( JSON.stringify( - lib_plankton.rest.to_oas(rest_subject), + lib_plankton.rest_caldav.to_oas(rest_subject), undefined, "\t" ) @@ -376,11 +376,11 @@ async function main( await _zeitbild.auth.init(); - const rest_subject : lib_plankton.rest.type_rest = _zeitbild.api.make(); + const rest_subject : lib_plankton.rest_caldav.type_rest = _zeitbild.api.make(); const server : lib_plankton.server.type_subject = lib_plankton.server.make( async (input, metadata) => { - const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(input.toString()); - const http_response : lib_plankton.http.type_response = await lib_plankton.rest.call( + const http_request : lib_plankton.caldav.type_request = lib_plankton.caldav.decode_request(input.toString()); + const http_response : lib_plankton.caldav.type_response = await lib_plankton.rest_caldav.call( rest_subject, http_request, { @@ -389,7 +389,7 @@ async function main( // "checklevel_output": lib_plankton.api.enum_checklevel.soft, } ); - const output : string = lib_plankton.http.encode_response(http_response); + const output : string = lib_plankton.caldav.encode_response(http_response); return output; }, { diff --git a/tools/makefile b/tools/makefile index b5ea7a9..ebedf93 100644 --- a/tools/makefile +++ b/tools/makefile @@ -75,7 +75,9 @@ ${dir_temp}/zeitbild-unlinked.js: \ ${dir_source}/api/actions/calendar_event_change.ts \ ${dir_source}/api/actions/calendar_event_remove.ts \ ${dir_source}/api/actions/events.ts \ - ${dir_source}/api/actions/export_caldav.ts \ + ${dir_source}/api/actions/export_ical.ts \ + ${dir_source}/api/actions/caldav_probe.ts \ + ${dir_source}/api/actions/caldav_get.ts \ ${dir_source}/api/functions.ts \ ${dir_source}/main.ts @ ${cmd_log} "compile …" diff --git a/tools/update-plankton b/tools/update-plankton index beecb08..b62b0e6 100755 --- a/tools/update-plankton +++ b/tools/update-plankton @@ -21,15 +21,20 @@ 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" ## exec -- 2.47.3 From b5b5dde9e397a4036e396bf4e4cad4e88d953b8e Mon Sep 17 00:00:00 2001 From: Fenris Wolf Date: Sun, 10 Nov 2024 17:20:41 +0100 Subject: [PATCH 03/30] [res] --- lib/plankton/plankton.d.ts | 1033 +++++++------ lib/plankton/plankton.js | 2935 +++++++++++++++++++----------------- source/main.ts | 52 +- tools/update-plankton | 5 +- 4 files changed, 2122 insertions(+), 1903 deletions(-) diff --git a/lib/plankton/plankton.d.ts b/lib/plankton/plankton.d.ts index 03dd668..ff94071 100644 --- a/lib/plankton/plankton.d.ts +++ b/lib/plankton/plankton.d.ts @@ -1,11 +1,11 @@ /** * @author fenris */ -declare type int = number; +type int = number; /** * @author fenris */ -declare type float = number; +type float = number; declare var process: any; declare var require: any; declare class Buffer { @@ -22,7 +22,7 @@ declare namespace lib_plankton.base { /** * @author fenris */ -declare type type_pseudopointer = { +type type_pseudopointer = { value: type_value; }; /** @@ -355,36 +355,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 */ @@ -670,183 +640,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 { /** @@ -1564,6 +1662,9 @@ declare namespace lib_plankton.string { /** */ function slice(str: string, size: int): Array; + /** + */ + function capitalize(str: string): string; } /** * @deprecated @@ -2164,7 +2265,7 @@ declare namespace lib_plankton.storage.memory { clear(): Promise; write(key: any, value: any): Promise; delete(key: any): Promise; - read(key: any): Promise; + read(key: any): Promise>; search(term: any): Promise<{ key: string; preview: string; @@ -3095,7 +3196,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; /** @@ -3124,13 +3225,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 +3268,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 +3282,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 +3470,58 @@ declare namespace lib_plankton.ical { */ function ics_encode(vcalendar: type_vcalendar): string; } +declare namespace lib_plankton.http_base { + /** + * @author roydfalk + */ + type type_request = { + scheme: ("http" | "https"); + host: (null | string); + path: string; + version: string; + method: type_method; + query: (null | string); + headers: Record; + body: (null | Buffer); + }; + /** + * @author fenris + */ + type type_response = { + version: (null | string); + status_code: type_status_code; + headers: Record; + body: Buffer; + }; +} +declare namespace lib_plankton.http_base { + /** + * @author fenris + */ + function encode_request(encode_method: ((method: type_method) => string), request: type_request): string; + /** + * @author fenris + */ + function decode_request(decode_method: ((method_raw: string) => type_method), has_body: ((method: type_method) => boolean), request_raw: string): type_request; + /** + * @author fenris + */ + 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; + /** + * @author fenris + */ + 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,31 +3536,71 @@ declare namespace lib_plankton.http { patch = "patch" } /** - * @author roydfalk */ - type type_request_generic = { - scheme: ("http" | "https"); - host: (null | string); - path: string; - version: string; - method: type_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_request = type_request_generic; + type type_request = lib_plankton.http_base.type_request; /** * @author fenris */ - type type_response = { - version: (null | string); - status_code: int; - headers: Record; - body: Buffer; - }; + type type_response = lib_plankton.http_base.type_response; } declare namespace lib_plankton.http { /** @@ -3418,10 +3611,6 @@ declare namespace lib_plankton.http { * @author fenris */ function decode_method(method_raw: string): enum_method; - /** - * @author fenris - */ - function encode_request(request: type_request): string; /** * @author fenris */ @@ -3429,7 +3618,7 @@ declare namespace lib_plankton.http { /** * @author fenris */ - function decode_request_generic(decode_method_: ((method_raw: string) => type_method), has_body: ((method: type_method) => boolean), request_raw: string): type_request_generic; + function encode_request(request: type_request): string; /** * @author fenris */ @@ -3447,52 +3636,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; - } -} /** * @author fenris */ @@ -3572,7 +3721,7 @@ declare namespace lib_plankton.xml { } declare namespace lib_plankton.webdav { /** - * @author roydfalk + * @todo try to base on lib_plankton.http.enum_method */ enum enum_method { options = "options", @@ -3591,17 +3740,74 @@ declare namespace lib_plankton.webdav { unlock = "unlock" } /** - * @author roydfalk + * @todo try to base on lib_plankton.http.enum_status_code + */ + 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, + multistatus = 207, + 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 + } + /** * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_href */ type type_data_href = string; /** - * @author roydfalk * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_status */ type type_data_status = string; /** - * @author roydfalk * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_prop */ type type_data_prop = { @@ -3609,7 +3815,6 @@ declare namespace lib_plankton.webdav { value: (null | string); }; /** - * @author roydfalk * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_propstat */ type type_data_propstat = { @@ -3618,7 +3823,6 @@ declare namespace lib_plankton.webdav { description: (null | string); }; /** - * @author roydfalk * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_response */ type type_data_response = { @@ -3632,7 +3836,6 @@ declare namespace lib_plankton.webdav { description: (null | string); }; /** - * @author roydfalk * @see http://www.webdav.org/specs/rfc2518.html#ELEMENT_multistatus */ type type_data_multistatus = { @@ -3640,18 +3843,11 @@ declare namespace lib_plankton.webdav { description: (null | string); }; /** - * @author roydfalk */ - type type_request = lib_plankton.http.type_request_generic; + type type_request = lib_plankton.http_base.type_request; /** - * @author roydfalk */ - type type_response = { - version: (null | string); - status_code: int; - headers: Record; - body: Buffer; - }; + type type_response = lib_plankton.http_base.type_response; } declare namespace lib_plankton.webdav { /** @@ -3667,25 +3863,28 @@ declare namespace lib_plankton.webdav { */ function decode_method(method_raw: string): enum_method; /** - */ - function data_multistatus_encode(data_multistatus: type_data_multistatus): string; - /** - * @author roydfalk + * @todo check */ function has_body(method: enum_method): boolean; /** - * @author fenris */ function decode_request(request_raw: string): type_request; /** - * @author fenris - * @todo try to base on lib_plankton.http.encode_response */ function encode_response(response: type_response): string; + /** + */ + function decode_response(response_raw: string): type_response; + /** + */ + function encode_request(request: type_request): string; + /** + */ + function data_multistatus_encode(data_multistatus: type_data_multistatus): string; } declare namespace lib_plankton.caldav { /** - * @author roydfalk + * @todo try to base on lib_plankton.webdav.enum_method */ enum enum_method { options = "options", @@ -3707,45 +3906,98 @@ declare namespace lib_plankton.caldav { acl = "acl" } /** - * @author roydfalk + * @todo try to base on lib_plankton.webdav.enum_status_code */ - type type_request = lib_plankton.http.type_request_generic; + 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, + multistatus = 207, + 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 roydfalk */ - type type_response = { - version: (null | string); - status_code: int; - headers: Record; - body: Buffer; - }; + type type_request = lib_plankton.http_base.type_request; + /** + */ + type type_response = lib_plankton.http_base.type_response; } declare namespace lib_plankton.caldav { /** - * @author roydfalk */ function is_special_method(method: enum_method): boolean; /** - * @author roydfalk */ function encode_method(method: enum_method): string; /** - * @author roydfalk */ function decode_method(method_raw: string): enum_method; /** - * @author roydfalk + * @todo check */ function has_body(method: enum_method): boolean; /** - * @author fenris + */ + function encode_request(request: type_request): string; + /** */ function decode_request(request_raw: string): type_request; /** - * @author fenris - * @todo try to base on lib_plankton.http.encode_response */ function encode_response(response: type_response): string; + /** + */ + function decode_response(response_raw: string): type_response; } declare namespace lib_plankton.markdown { /** @@ -4064,221 +4316,15 @@ declare namespace lib_plankton.rest_base { * @todo check request body mimetype? * @todo check query paramater validity */ - function call>(encode_http_method: ((http_method: type_http_method) => string), rest: type_rest, http_request: type_http_request, options?: { + function call(encode_http_method: ((http_method: type_http_method) => string), 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, }?: { checklevel_restriction?: lib_plankton.api.enum_checklevel; checklevel_input?: lib_plankton.api.enum_checklevel; checklevel_output?: lib_plankton.api.enum_checklevel; - }): Promise; + }): Promise>; /** * @see https://swagger.io/specification/#openrest-object */ - function to_oas(http_request_method_to_oas: ((http_request_method: type_method) => string), rest: type_rest, options?: { - 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?: { - 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"; - parameters: {}; - } | { - kind: "key_header"; - parameters: { - 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); - }; - }>; - }): 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; - /** - * @todo check request body mimetype? - * @todo check query paramater validity - */ - function call(rest: type_rest, http_request: lib_plankton.http.type_request, options?: { - checklevel_restriction?: lib_plankton.api.enum_checklevel; - checklevel_input?: lib_plankton.api.enum_checklevel; - checklevel_output?: lib_plankton.api.enum_checklevel; - }): Promise; - /** - * @see https://swagger.io/specification/#openrest-object - */ - function to_oas(rest: type_rest, options?: { - version?: (null | string); - servers?: Array; - }): any; -} -declare namespace lib_plankton.rest_webdav { - /** - */ - 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_webdav { - /** - */ - function make(options?: { - 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"; - parameters: {}; - } | { - kind: "key_header"; - parameters: { - name: string; - }; - }); - actions?: Array<{ - http_method: lib_plankton.webdav.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); - }; - }>; - }): type_rest; - /** - */ - function register(rest: type_rest, http_method: lib_plankton.webdav.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; - /** - * @todo check request body mimetype? - * @todo check query paramater validity - */ - function call(rest: type_rest, http_request: lib_plankton.webdav.type_request, options?: { - checklevel_restriction?: lib_plankton.api.enum_checklevel; - checklevel_input?: lib_plankton.api.enum_checklevel; - checklevel_output?: lib_plankton.api.enum_checklevel; - }): Promise; - /** - * @see https://swagger.io/specification/#openrest-object - */ - function to_oas(rest: type_rest, options?: { + function to_oas(http_request_method_to_oas: ((http_request_method: type_method) => string), has_body: ((method: type_method) => boolean), rest: type_rest, options?: { version?: (null | string); servers?: Array; }): any; @@ -4372,6 +4418,7 @@ declare namespace lib_plankton.rest_caldav { /** * @todo check request body mimetype? * @todo check query paramater validity + * @todo improve status code mapping */ function call(rest: type_rest, http_request: lib_plankton.caldav.type_request, options?: { checklevel_restriction?: lib_plankton.api.enum_checklevel; diff --git a/lib/plankton/plankton.js b/lib/plankton/plankton.js index dec742c..bda12c3 100644 --- a/lib/plankton/plankton.js +++ b/lib/plankton/plankton.js @@ -776,89 +776,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 . */ @@ -1527,7 +1444,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); - while (_) try { + while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { @@ -1606,21 +1523,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 +1557,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 +1590,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 +1625,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": timestamp.toFixed(0), + "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 +2233,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' @@ -4629,6 +4830,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 { @@ -4929,6 +5131,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 = {})); /** @@ -6533,7 +6741,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); - while (_) try { + while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { @@ -10138,7 +10346,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); - while (_) try { + while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { @@ -10647,15 +10855,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)), @@ -10859,12 +11067,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 +11091,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 +11229,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 +11244,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 +11257,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 +11271,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 +11285,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 +11295,7 @@ var lib_plankton; ? null : - (adjust_to_ce + (option_adjust_to_ce ? datetime_translate_ce(to) : @@ -11102,7 +11304,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 +11319,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), }) : @@ -12063,6 +12265,327 @@ var lib_plankton; })(ical = lib_plankton.ical || (lib_plankton.ical = {})); })(lib_plankton || (lib_plankton = {})); /* +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) { + /** + * @author fenris + */ + const linebreak = "\r\n"; + /** + */ + function capitalize_all(str) { + return str.split("-").map(x => lib_plankton.string.capitalize(x)).join("-"); + } + /** + * @author fenris + */ + 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; + /** + * @author fenris + */ + function decode_request(decode_method, has_body, 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 = (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; + /** + * @author fenris + */ + 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; + response_raw += response.body.toString(); + return response_raw; + } + http_base.encode_response = encode_response; + /** + * @author fenris + */ + function decode_response(decode_status_code, 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 = decode_status_code(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; + } + 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, + }, (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«. Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' @@ -12098,6 +12621,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 +12705,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 */ @@ -12171,10 +12737,21 @@ var lib_plankton; } } 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"; @@ -12233,43 +12810,6 @@ var lib_plankton; default: throw (new Error("unhandled statuscode: " + statuscode.toFixed(0))); } } - /** - * @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; - } - http.encode_request = encode_request; /** * @author fenris */ @@ -12284,107 +12824,29 @@ var lib_plankton; /** * @author fenris */ - function decode_request_generic(decode_method_, has_body, 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 = (has_body(method) - // @ts-ignore - ? Buffer.from(lines.join(linebreak)) - : null); - const request_generic = { - // TODO - "scheme": "http", - "host": (headers["host"] ?? null), - "path": path, - "version": version, - "method": method, - "query": query, - "headers": headers, - "body": body, - }; - return request_generic; + function encode_request(request) { + return lib_plankton.http_base.encode_request(encode_method, request); } - http.decode_request_generic = decode_request_generic; + http.encode_request = encode_request; /** * @author fenris */ function decode_request(request_raw) { - return decode_request_generic(decode_method, has_body, request_raw); + 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; /** @@ -12392,240 +12854,16 @@ 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 = {})); var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || @@ -12790,7 +13028,7 @@ var lib_plankton; var webdav; (function (webdav) { /** - * @author roydfalk + * @todo try to base on lib_plankton.http.enum_method */ let enum_method; (function (enum_method) { @@ -12810,6 +13048,67 @@ var lib_plankton; enum_method["unlock"] = "unlock"; })(enum_method = webdav.enum_method || (webdav.enum_method = {})); ; + /** + * @todo try to base on lib_plankton.http.enum_status_code + */ + 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["multistatus"] = 207] = "multistatus"; + 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 = webdav.enum_status_code || (webdav.enum_status_code = {})); })(webdav = lib_plankton.webdav || (lib_plankton.webdav = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -12835,22 +13134,6 @@ var lib_plankton; (function (lib_plankton) { var webdav; (function (webdav) { - /** - * @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 roydfalk */ @@ -12917,9 +13200,19 @@ var lib_plankton; } webdav.decode_method = decode_method; /** - * @author fenris */ - function get_statustext(statuscode) { + 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); + } + /** + */ + function get_status_text(statuscode) { switch (statuscode) { case 100: return "Continue"; case 101: return "Switching Protocols"; @@ -12979,6 +13272,47 @@ var lib_plankton; default: throw (new Error("unhandled statuscode: " + statuscode.toFixed(0))); } } + /** + * @todo check + */ + function has_body(method) { + return [ + webdav.enum_method.post, + webdav.enum_method.put, + webdav.enum_method.patch, + webdav.enum_method.propfind, + webdav.enum_method.proppatch, + webdav.enum_method.mkcol, + webdav.enum_method.copy, + webdav.enum_method.move, + webdav.enum_method.lock, + ].includes(method); + } + webdav.has_body = has_body; + /** + */ + function decode_request(request_raw) { + return lib_plankton.http_base.decode_request(decode_method, has_body, request_raw); + } + webdav.decode_request = decode_request; + /** + */ + function encode_response(response) { + return lib_plankton.http_base.encode_response(encode_status_code, get_status_text, response); + } + webdav.encode_response = encode_response; + /** + */ + function decode_response(response_raw) { + return lib_plankton.http_base.decode_response(decode_status_code, response_raw); + } + webdav.decode_response = decode_response; + /** + */ + function encode_request(request) { + return lib_plankton.http_base.encode_request(encode_method, request); + } + webdav.encode_request = encode_request; /** */ function data_href_encode_xml(data_href) { @@ -13041,56 +13375,6 @@ var lib_plankton; return data_multistatus_encode_xml(data_multistatus).compile(); } webdav.data_multistatus_encode = data_multistatus_encode; - /** - * @author roydfalk - */ - function has_body(method) { - return [ - webdav.enum_method.post, - webdav.enum_method.put, - webdav.enum_method.patch, - webdav.enum_method.propfind, - webdav.enum_method.proppatch, - webdav.enum_method.mkcol, - webdav.enum_method.copy, - webdav.enum_method.move, - webdav.enum_method.lock, - webdav.enum_method.unlock, // TODO verify - ].includes(method); - } - webdav.has_body = has_body; - /** - * @author fenris - */ - function decode_request(request_raw) { - return lib_plankton.http.decode_request_generic(decode_method, has_body, request_raw); - } - webdav.decode_request = decode_request; - /** - * @author fenris - * @todo try to base on lib_plankton.http.encode_response - */ - 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; - } - webdav.encode_response = encode_response; })(webdav = lib_plankton.webdav || (lib_plankton.webdav = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -13117,7 +13401,7 @@ var lib_plankton; var caldav; (function (caldav) { /** - * @author roydfalk + * @todo try to base on lib_plankton.webdav.enum_method */ let enum_method; (function (enum_method) { @@ -13140,6 +13424,67 @@ var lib_plankton; enum_method["acl"] = "acl"; })(enum_method = caldav.enum_method || (caldav.enum_method = {})); ; + /** + * @todo try to base on lib_plankton.webdav.enum_status_code + */ + 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["multistatus"] = 207] = "multistatus"; + 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 = caldav.enum_status_code || (caldav.enum_status_code = {})); })(caldav = lib_plankton.caldav || (lib_plankton.caldav = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -13166,34 +13511,24 @@ var lib_plankton; var caldav; (function (caldav) { /** - * @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 roydfalk */ function is_special_method(method) { - return ((method === caldav.enum_method.report) + return ((method === caldav.enum_method.propfind) || - (method === caldav.enum_method.mkcalendar) + (method === caldav.enum_method.proppatch) || - (method === caldav.enum_method.acl)); + (method === caldav.enum_method.mkcol) + || + (method === caldav.enum_method.copy) + || + (method === caldav.enum_method.move) + || + (method === caldav.enum_method.lock) + || + (method === caldav.enum_method.unlock)); } caldav.is_special_method = is_special_method; /** - * @author roydfalk */ function encode_method(method) { switch (method) { @@ -13211,15 +13546,11 @@ var lib_plankton; case caldav.enum_method.move: return "MOVE"; case caldav.enum_method.lock: return "LOCK"; case caldav.enum_method.unlock: return "UNLOCK"; - case caldav.enum_method.report: return "REPORT"; - case caldav.enum_method.mkcalendar: return "MKCALENDAR"; - case caldav.enum_method.acl: return "ACL"; default: throw (new Error("impossible")); } } caldav.encode_method = encode_method; /** - * @author roydfalk */ function decode_method(method_raw) { switch (method_raw) { @@ -13237,17 +13568,24 @@ var lib_plankton; case "MOVE": return caldav.enum_method.move; case "LOCK": return caldav.enum_method.lock; case "UNLOCK": return caldav.enum_method.unlock; - case "REPORT": return caldav.enum_method.report; - case "MKCALENDAR": return caldav.enum_method.mkcalendar; - case "ACL": return caldav.enum_method.acl; default: throw (new Error("unhandled method: " + method_raw)); } } caldav.decode_method = decode_method; /** - * @author fenris */ - function get_statustext(statuscode) { + 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); + } + /** + */ + function get_status_text(statuscode) { switch (statuscode) { case 100: return "Continue"; case 101: return "Switching Protocols"; @@ -13308,7 +13646,7 @@ var lib_plankton; } } /** - * @author roydfalk + * @todo check */ function has_body(method) { return [ @@ -13321,43 +13659,33 @@ var lib_plankton; caldav.enum_method.copy, caldav.enum_method.move, caldav.enum_method.lock, - caldav.enum_method.unlock, - caldav.enum_method.mkcalendar, // TODO verify ].includes(method); } caldav.has_body = has_body; /** - * @author fenris + */ + function encode_request(request) { + return lib_plankton.http_base.encode_request(encode_method, request); + } + caldav.encode_request = encode_request; + /** */ function decode_request(request_raw) { - return lib_plankton.http.decode_request_generic(decode_method, has_body, request_raw); + return lib_plankton.http_base.decode_request(decode_method, has_body, request_raw); } caldav.decode_request = decode_request; /** - * @author fenris - * @todo try to base on lib_plankton.http.encode_response */ 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); } caldav.encode_response = encode_response; + /** + */ + function decode_response(response_raw) { + return lib_plankton.http_base.decode_response(decode_status_code, response_raw); + } + caldav.decode_response = decode_response; })(caldav = lib_plankton.caldav || (lib_plankton.caldav = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -14123,12 +14451,7 @@ var lib_plankton; * @todo check request body mimetype? * @todo check query paramater validity */ - async function call(encode_http_method, 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); + async function call(encode_http_method, 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, } = {}) { lib_plankton.log.info("rest_call", { "http_request": { "scheme": http_request.scheme, @@ -14205,16 +14528,16 @@ var lib_plankton; if (stuff.rest.length > 0) { return { "version": "HTTP/1.1", - "status_code": 404, + "status_code": decode_status_code(404), "headers": {}, "body": null, }; } else { - if (http_request.method === lib_plankton.http.enum_method.options) { + if (http_request.method === "OPTIONS") { return { "version": "HTTP/1.1", - "status_code": 200, + "status_code": decode_status_code(200), "headers": Object.assign({}, additional_response_headers), "body": null, }; @@ -14225,7 +14548,7 @@ var lib_plankton; if (Object.keys(stuff.routenode.operations).length <= 0) { return { "version": "HTTP/1.1", - "status_code": 404, + "status_code": decode_status_code(404), "headers": Object.assign({}, additional_response_headers), "body": null, }; @@ -14233,7 +14556,7 @@ var lib_plankton; else { return { "version": "HTTP/1.1", - "status_code": 405, + "status_code": decode_status_code(405), "headers": Object.assign({ "Allow": allowed_methods, }, additional_response_headers), @@ -14305,9 +14628,9 @@ var lib_plankton; "query_parameters": stuff_.query_parameters, }, "input": stuff_.input, - "checklevel_restriction": options.checklevel_restriction, - "checklevel_input": options.checklevel_input, - "checklevel_output": options.checklevel_output, + "checklevel_restriction": option_checklevel_restriction, + "checklevel_input": option_checklevel_input, + "checklevel_output": option_checklevel_output, }); error = null; } @@ -14319,7 +14642,7 @@ var lib_plankton; if (error instanceof lib_plankton.api.class_error_permission_denied) { response = { "version": "HTTP/1.1", - "status_code": 403, + "status_code": decode_status_code(403), "headers": Object.assign({}, additional_response_headers), "body": null, }; @@ -14346,7 +14669,7 @@ var lib_plankton; }); response = { "version": "HTTP/1.1", - "status_code": 500, + "status_code": decode_status_code(500), "headers": Object.assign({}, additional_response_headers), "body": Buffer["from"]("internal error"), }; @@ -14356,7 +14679,7 @@ var lib_plankton; // encode response = { "version": "HTTP/1.1", - "status_code": result.status_code, + "status_code": decode_status_code(result.status_code), "headers": Object.assign({ "Content-Type": operation.response_body_mimetype, }, additional_response_headers), @@ -14374,7 +14697,7 @@ var lib_plankton; /** * @see https://swagger.io/specification/#openrest-object */ - function to_oas(http_request_method_to_oas, rest, options = {}) { + function to_oas(http_request_method_to_oas, has_body, rest, options = {}) { options = lib_plankton.object.patched({ "version": null, "servers": [], @@ -14469,11 +14792,7 @@ var lib_plankton; }, "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([ @@ -14513,183 +14832,6 @@ var lib_plankton; })(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 - */ - async function call(rest, http_request, options = {}) { - return lib_plankton.rest_base.call(lib_plankton.http.encode_method, rest, http_request, options); - } - 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, 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:rest_webdav«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:rest_webdav« 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_webdav« 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_webdav«. If not, see . - */ -/* -This file is part of »bacterio-plankton:rest_webdav«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:rest_webdav« 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_webdav« 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_webdav«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var rest_webdav; - (function (rest_webdav) { - /** - */ - function http_request_method_to_oas(http_request_method) { - switch (http_request_method) { - case lib_plankton.webdav.enum_method.get: return "get"; - case lib_plankton.webdav.enum_method.post: return "post"; - case lib_plankton.webdav.enum_method.patch: return "patch"; - case lib_plankton.webdav.enum_method.head: return "head"; - case lib_plankton.webdav.enum_method.delete: return "delete"; - case lib_plankton.webdav.enum_method.options: return "options"; - case lib_plankton.webdav.enum_method.put: return "put"; - case lib_plankton.webdav.enum_method.propfind: return "PROPFIND"; - case lib_plankton.webdav.enum_method.proppatch: return "PROPPATCH"; - case lib_plankton.webdav.enum_method.mkcol: return "MKCOL"; - case lib_plankton.webdav.enum_method.copy: return "COPY"; - case lib_plankton.webdav.enum_method.move: return "MOVE"; - case lib_plankton.webdav.enum_method.lock: return "LOCK"; - case lib_plankton.webdav.enum_method.unlock: return "UNLOCK"; - default: throw (new Error("impossible")); - } - } - /** - */ - function make(options = {}) { - return lib_plankton.rest_base.make(lib_plankton.webdav.encode_method, options); - } - rest_webdav.make = make; - /** - */ - function register(rest, http_method, path, options) { - lib_plankton.rest_base.register(lib_plankton.webdav.encode_method, rest, http_method, path, options); - } - rest_webdav.register = register; - /** - * @todo check request body mimetype? - * @todo check query paramater validity - */ - async function call(rest, http_request, options = {}) { - return lib_plankton.rest_base.call(lib_plankton.webdav.encode_method, rest, http_request, options); - } - rest_webdav.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, rest, options); - } - rest_webdav.to_oas = to_oas; - })(rest_webdav = lib_plankton.rest_webdav || (lib_plankton.rest_webdav = {})); -})(lib_plankton || (lib_plankton = {})); -/* This file is part of »bacterio-plankton:rest_caldav«. Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' @@ -14742,16 +14884,16 @@ var lib_plankton; case lib_plankton.caldav.enum_method.delete: return "delete"; case lib_plankton.caldav.enum_method.options: return "options"; case lib_plankton.caldav.enum_method.put: return "put"; - case lib_plankton.caldav.enum_method.propfind: return "PROPFIND"; - case lib_plankton.caldav.enum_method.proppatch: return "PROPPATCH"; - case lib_plankton.caldav.enum_method.mkcol: return "MKCOL"; - case lib_plankton.caldav.enum_method.copy: return "COPY"; - case lib_plankton.caldav.enum_method.move: return "MOVE"; - case lib_plankton.caldav.enum_method.lock: return "LOCK"; - case lib_plankton.caldav.enum_method.unlock: return "UNLOCK"; - case lib_plankton.caldav.enum_method.report: return "REPORT"; - case lib_plankton.caldav.enum_method.mkcalendar: return "MKCALENDAR"; - case lib_plankton.caldav.enum_method.acl: return "ACL"; + case lib_plankton.caldav.enum_method.propfind: return "propfind"; + case lib_plankton.caldav.enum_method.proppatch: return "proppatch"; + case lib_plankton.caldav.enum_method.mkcol: return "mkcol"; + case lib_plankton.caldav.enum_method.copy: return "copy"; + case lib_plankton.caldav.enum_method.move: return "move"; + case lib_plankton.caldav.enum_method.lock: return "lock"; + case lib_plankton.caldav.enum_method.unlock: return "unlock"; + case lib_plankton.caldav.enum_method.report: return "report"; + case lib_plankton.caldav.enum_method.mkcalendar: return "mkcalendar"; + case lib_plankton.caldav.enum_method.acl: return "acl"; default: throw (new Error("impossible")); } } @@ -14770,16 +14912,17 @@ var lib_plankton; /** * @todo check request body mimetype? * @todo check query paramater validity + * @todo improve status code mapping */ async function call(rest, http_request, options = {}) { - return lib_plankton.rest_base.call(lib_plankton.caldav.encode_method, rest, http_request, options); + return lib_plankton.rest_base.call(lib_plankton.caldav.encode_method, (x => x), rest, http_request, options); } rest_caldav.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, rest, options); + return lib_plankton.rest_base.to_oas(http_request_method_to_oas, lib_plankton.caldav.has_body, rest, options); } rest_caldav.to_oas = to_oas; })(rest_caldav = lib_plankton.rest_caldav || (lib_plankton.rest_caldav = {})); @@ -15812,7 +15955,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); - while (_) try { + while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { diff --git a/source/main.ts b/source/main.ts index b53b44d..9acebe2 100644 --- a/source/main.ts +++ b/source/main.ts @@ -159,11 +159,17 @@ async function main( ) : Promise { // init1 - lib_plankton.log.conf_push( + /* + lib_plankton.log.set_main_logger( [ - lib_plankton.log.channel_make({"kind": "stdout", "data": {"threshold": "info"}}), + { + "kind": "std", + "data": { + } + } ] ); + */ // args const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler({ @@ -246,20 +252,45 @@ async function main( "name": "help", }), }); - const args : Record = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" ")); + const args : Record = arg_handler.read( + lib_plankton.args.enum_environment.cli, + args_raw.join(" ") + ); // init2 await _zeitbild.conf.init( args["conf_path"] ); - lib_plankton.log.conf_push( + lib_plankton.log.set_main_logger( _zeitbild.conf.get().log.map( - (log_output : any) => lib_plankton.log.channel_make( - { - "kind": log_output.kind, - "data": log_output.data + (log_output : any) => { + switch (log_output.kind) { + case "stdout": { + return { + "kind": "minlevel", + "data": { + "core": { + "kind": "std", + "data": { + "target": "stdout", + "format": { + "kind": "human_readable", + "data": { + } + } + } + }, + "threshold": log_output.data.threshold, + } + }; + break; + } + default: { + throw (new Error("unhandled")); + break; + } } - ) + } ) ); _zeitbild.cache = lib_plankton.cache.chest.implementation( @@ -321,9 +352,8 @@ async function main( break; } case "api-doc": { - lib_plankton.log.conf_push([]); + lib_plankton.log.set_main_logger([]); const rest_subject : lib_plankton.rest_caldav.type_rest = _zeitbild.api.make(); - lib_plankton.log.conf_pop(); process.stdout.write( JSON.stringify( lib_plankton.rest_caldav.to_oas(rest_subject), diff --git a/tools/update-plankton b/tools/update-plankton index b62b0e6..b74d7d8 100755 --- a/tools/update-plankton +++ b/tools/update-plankton @@ -24,9 +24,8 @@ modules="${modules} http" modules="${modules} webdav" modules="${modules} caldav" modules="${modules} api" -modules="${modules} rest" -modules="${modules} rest_http" -modules="${modules} rest_webdav" +# modules="${modules} rest_http" +# modules="${modules} rest_webdav" modules="${modules} rest_caldav" modules="${modules} server" modules="${modules} args" -- 2.47.3 From 54e27f2ad97bc0d5fc5ddd7f0f34ddf62f332d99 Mon Sep 17 00:00:00 2001 From: Fenris Wolf Date: Thu, 14 Nov 2024 19:57:13 +0100 Subject: [PATCH 04/30] [int] --- conf/example.json | 3 +- lib/plankton/plankton.d.ts | 56 ++- lib/plankton/plankton.js | 542 +++++++++++++++++------------ source/api/actions/caldav_probe.ts | 110 +++--- source/api/actions/caldav_sniff.ts | 46 +++ source/api/base.ts | 91 +++++ source/api/functions.ts | 1 + source/conf.ts | 12 +- source/main.ts | 30 +- source/services/calendar.ts | 8 +- tools/makefile | 1 + 11 files changed, 589 insertions(+), 311 deletions(-) create mode 100644 source/api/actions/caldav_sniff.ts diff --git a/conf/example.json b/conf/example.json index 5821d0b..ea0df88 100644 --- a/conf/example.json +++ b/conf/example.json @@ -4,7 +4,8 @@ { "kind": "stdout", "data": { - "threshold": "info" + "threshold": "info", + "format": "human_readable" } } ], diff --git a/lib/plankton/plankton.d.ts b/lib/plankton/plankton.d.ts index ff94071..355d916 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 { /** @@ -619,6 +625,12 @@ declare namespace lib_plankton.call { value: (null | type_value); error: (null | any); }; + /** + */ + function try_catch_wrap_async(get_value: (() => Promise)): Promise<{ + value: (null | type_value); + error: (null | any); + }>; } declare namespace lib_plankton.email { /** @@ -3472,7 +3484,6 @@ declare namespace lib_plankton.ical { } declare namespace lib_plankton.http_base { /** - * @author roydfalk */ type type_request = { scheme: ("http" | "https"); @@ -3485,30 +3496,25 @@ declare namespace lib_plankton.http_base { body: (null | Buffer); }; /** - * @author fenris */ type type_response = { version: (null | string); status_code: type_status_code; headers: Record; - body: Buffer; + body: (null | Buffer); }; } declare namespace lib_plankton.http_base { /** - * @author fenris */ function encode_request(encode_method: ((method: type_method) => string), request: type_request): string; /** - * @author fenris */ function decode_request(decode_method: ((method_raw: string) => type_method), has_body: ((method: type_method) => boolean), request_raw: string): type_request; /** - * @author fenris */ 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; /** - * @author fenris */ function decode_response(decode_status_code: ((status_code_raw: string) => type_status_code), response_raw: string): type_response; /** @@ -3642,79 +3648,60 @@ declare namespace lib_plankton.http { implementation?: ("fetch" | "http_module"); }): Promise; } -/** - * @author fenris - */ declare namespace lib_plankton.xml { /** - * @author fenris */ abstract class class_node { /** - * @author fenris */ abstract compile(depth?: int): string; } /** - * @author fenris */ class class_node_text extends class_node { /** - * @author fenris */ protected content: string; /** - * @author fenris */ constructor(content: string); /** - * @author fenris */ compile(depth?: int): string; } /** - * @author fenris */ class class_node_comment extends class_node { /** - * @author fenris */ protected content: string; /** - * @author fenris */ constructor(content: string); /** - * @author fenris */ compile(depth?: int): string; } /** - * @author fenris */ class class_node_complex extends class_node { /** - * @author fenris */ protected name: string; /** - * @author fenris */ protected attributes: { [key: string]: string; }; /** - * @author fenris */ protected children: Array; /** - * @author fenris */ constructor(name: string, attributes?: { [key: string]: string; }, children?: any[]); /** - * @author fenris */ compile(depth?: int): string; } @@ -4186,6 +4173,7 @@ declare namespace lib_plankton.rest_base { }) => Promise<{ status_code: int; data: type_output; + headers?: (null | Record); }>); /** */ @@ -4209,7 +4197,7 @@ declare namespace lib_plankton.rest_base { 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); + response_body_encode: ((output: any) => (null | Buffer)); }; /** */ @@ -4316,15 +4304,16 @@ declare namespace lib_plankton.rest_base { * @todo check request body mimetype? * @todo check query paramater validity */ - function call(encode_http_method: ((http_method: type_http_method) => string), 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, }?: { + 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, options?: { + 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; @@ -4420,10 +4409,11 @@ declare namespace lib_plankton.rest_caldav { * @todo check query paramater validity * @todo improve status code mapping */ - function call(rest: type_rest, http_request: lib_plankton.caldav.type_request, options?: { + function call(rest: type_rest, http_request: lib_plankton.caldav.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 @@ -4435,13 +4425,11 @@ declare namespace lib_plankton.rest_caldav { } declare namespace lib_plankton.server { /** - * @author fenris */ type type_metadata = { ip_address: string; }; /** - * @author fenris */ type type_subject = { host: string; @@ -4451,7 +4439,6 @@ declare namespace lib_plankton.server { serverobj: any; }; /** - * @author fenris */ function make(handle: ((input: string, metadata?: type_metadata) => Promise), options?: { host?: string; @@ -4459,17 +4446,14 @@ declare namespace lib_plankton.server { threshold?: (null | float); }): type_subject; /** - * @author fenris * @deprecated */ function make_old(port: int, handle: ((input: string, metadata?: type_metadata) => Promise)): type_subject; /** - * @author fenris * @see https://nodejs.org/api/net.html#serverlistenport-host-backlog-callback */ function start(subject: type_subject): Promise; /** - * @author fenris */ function kill(subject: type_subject): void; } diff --git a/lib/plankton/plankton.js b/lib/plankton/plankton.js index bda12c3..c5cc5ee 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 = {})); /* @@ -1427,6 +1420,20 @@ 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; })(call = lib_plankton.call || (lib_plankton.call = {})); })(lib_plankton || (lib_plankton = {})); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { @@ -1667,7 +1674,7 @@ var lib_plankton; var timestamp = (now / 1000); var datetime = (new Date(now)).toISOString(); return (JSON.stringify({ - "datetime_timestamp": timestamp.toFixed(0), + "datetime_timestamp": Math.round(timestamp), "datetime_string": datetime /*.slice(0, 19)*/, "level_numeric": entry.level, "level_name": log.level_show(entry.level, { "abbreviated": false }), @@ -12264,6 +12271,7 @@ 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«. @@ -12307,7 +12315,6 @@ var lib_plankton; var http_base; (function (http_base) { /** - * @author fenris */ const linebreak = "\r\n"; /** @@ -12316,7 +12323,6 @@ var lib_plankton; return str.split("-").map(x => lib_plankton.string.capitalize(x)).join("-"); } /** - * @author fenris */ function encode_request(encode_method, request) { let request_raw = ""; @@ -12354,11 +12360,11 @@ var lib_plankton; } http_base.encode_request = encode_request; /** - * @author fenris */ function decode_request(decode_method, has_body, request_raw) { const lines = request_raw.split(linebreak); - const first = lines.shift(); + const first = lines[0]; + lines.shift(); const parts = first.split(" "); const method = decode_method(parts[0]); const path_and_query = parts[1]; @@ -12368,7 +12374,8 @@ var lib_plankton; const version = parts[2]; let headers = {}; while (true) { - const line = lines.shift(); + const line = lines[0]; + lines.shift(); if (line === "") { break; } @@ -12396,12 +12403,11 @@ var lib_plankton; } http_base.decode_request = decode_request; /** - * @author fenris */ 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, + "version": (response.version ?? ""), "status_code": encode_status_code(response.status_code), "status_text": get_status_text(response.status_code), "linebreak": linebreak, @@ -12414,23 +12420,29 @@ var lib_plankton; }); } response_raw += linebreak; - response_raw += response.body.toString(); + if (response.body === null) { + // do nothing + } + else { + response_raw += response.body.toString(); + } return response_raw; } http_base.encode_response = encode_response; /** - * @author fenris */ function decode_response(decode_status_code, response_raw) { const lines = response_raw.split(linebreak); - const first = lines.shift(); + 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.shift(); + const line = lines[0]; + lines.shift(); if (line === "") { break; } @@ -12459,7 +12471,7 @@ var lib_plankton; 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, + "host": (request.host ?? ""), "path": request.path, "query": (request.query ?? ""), }); @@ -12551,7 +12563,9 @@ var lib_plankton; .request(target, { "method": request.method, "headers": request.headers, - }, (res) => { + }, + // @ts-ignore + (res) => { try { let response_body = ""; res.setEncoding("utf8"); @@ -12898,21 +12912,16 @@ 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:xml«. If not, see . */ -/** - * @author fenris - */ var lib_plankton; (function (lib_plankton) { var xml; (function (xml) { /** - * @author fenris */ function string_repeat(symbol, count) { return ((count <= 0) ? "" : (string_repeat(symbol, count - 1) + symbol)); } /** - * @author fenris */ var class_node = /** @class */ (function () { function class_node() { @@ -12921,12 +12930,10 @@ var lib_plankton; }()); xml.class_node = class_node; /** - * @author fenris */ var class_node_text = /** @class */ (function (_super) { __extends(class_node_text, _super); /** - * @author fenris */ function class_node_text(content) { var _this = _super.call(this) || this; @@ -12934,22 +12941,19 @@ var lib_plankton; return _this; } /** - * @author fenris */ class_node_text.prototype.compile = function (depth) { if (depth === void 0) { depth = 0; } - return (string_repeat("\t", depth) + this.content + "\n"); + return (string_repeat("\t", depth) + this.content /* + "\n"*/); }; return class_node_text; }(class_node)); xml.class_node_text = class_node_text; /** - * @author fenris */ var class_node_comment = /** @class */ (function (_super) { __extends(class_node_comment, _super); /** - * @author fenris */ function class_node_comment(content) { var _this = _super.call(this) || this; @@ -12957,7 +12961,6 @@ var lib_plankton; return _this; } /** - * @author fenris */ class_node_comment.prototype.compile = function (depth) { if (depth === void 0) { depth = 0; } @@ -12967,12 +12970,10 @@ var lib_plankton; }(class_node)); xml.class_node_comment = class_node_comment; /** - * @author fenris */ var class_node_complex = /** @class */ (function (_super) { __extends(class_node_complex, _super); /** - * @author fenris */ function class_node_complex(name, attributes, children) { if (attributes === void 0) { attributes = {}; } @@ -12984,7 +12985,6 @@ var lib_plankton; return _this; } /** - * @author fenris */ class_node_complex.prototype.compile = function (depth) { var _this = this; @@ -12994,9 +12994,24 @@ var lib_plankton; .filter(function (key) { return (_this.attributes[key] !== null); }) .map(function (key) { return (" " + key + "=" + ("\"" + _this.attributes[key] + "\"")); }) .join("")); - output += (string_repeat("\t", depth) + "<" + this.name + attributes + ">" + "\n"); - this.children.forEach(function (child) { return (output += child.compile(depth + 1)); }); - output += (string_repeat("\t", depth) + "" + "\n"); + if ((this.children.length === 1) + && + (this.children[0] instanceof class_node_text)) { + output += (string_repeat("\t", depth) + + + ("<" + this.name + attributes + ">") + + + this.children[0].compile(0) + + + ("") + + + "\n"); + } + else { + output += (string_repeat("\t", depth) + "<" + this.name + attributes + ">" + "\n"); + this.children.forEach(function (child) { return (output += child.compile(depth + 1)); }); + output += (string_repeat("\t", depth) + "" + "\n"); + } return output; }; return class_node_complex; @@ -13316,31 +13331,33 @@ var lib_plankton; /** */ function data_href_encode_xml(data_href) { - return (new lib_plankton.xml.class_node_complex("d:href", {}, [ + return (new lib_plankton.xml.class_node_complex("D:href", {}, [ new lib_plankton.xml.class_node_text(data_href), ])); } /** */ function data_status_encode_xml(data_status) { - return (new lib_plankton.xml.class_node_complex("d:status", {}, [ + return (new lib_plankton.xml.class_node_complex("D:status", {}, [ new lib_plankton.xml.class_node_text(data_status), ])); } /** */ function data_prop_encode_xml(data_prop) { - return (new lib_plankton.xml.class_node_complex(data_prop.name, {}, [ - new lib_plankton.xml.class_node_text(data_prop.value ?? ""), + return (new lib_plankton.xml.class_node_complex(("D:" + data_prop.name), {}, [ + new lib_plankton.xml.class_node_text(data_prop.value + ?? + ""), ])); } /** */ function data_propstat_encode_xml(data_propstat) { - return (new lib_plankton.xml.class_node_complex("d:propstat", { + return (new lib_plankton.xml.class_node_complex("D:propstat", { // todo xmlns:R }, [ - new lib_plankton.xml.class_node_complex("d:prop", { + new lib_plankton.xml.class_node_complex("D:prop", { // todo xmlns:R }, data_propstat.prop.map(data_prop_encode_xml)), data_status_encode_xml(data_propstat.status), @@ -13349,7 +13366,7 @@ var lib_plankton; /** */ function data_response_encode_xml(data_response) { - return (new lib_plankton.xml.class_node_complex("d:response", {}, ([ + return (new lib_plankton.xml.class_node_complex("D:response", {}, ([ data_href_encode_xml(data_response.href), ] .concat(("hrefs" in data_response.body) @@ -13362,12 +13379,14 @@ var lib_plankton; data_response.body.propstats.map(data_propstat_encode_xml))))); } /** + * @todo description */ function data_multistatus_encode_xml(data_multistatus) { - return (new lib_plankton.xml.class_node_complex("d:multistatus", { - "xmlns:d": "DAV:", - }, (data_multistatus.responses.map(data_response_encode_xml) - .concat()))); + return (new lib_plankton.xml.class_node_complex("D:multistatus", { + "xmlns:D": "DAV:", + "xmlns:C": "urn:ietf:params:xml:ns:caldav", + "xmlns:CS": "http://calendarserver.org/ns/", + }, data_multistatus.responses.map(data_response_encode_xml))); } /** */ @@ -13546,7 +13565,10 @@ var lib_plankton; case caldav.enum_method.move: return "MOVE"; case caldav.enum_method.lock: return "LOCK"; case caldav.enum_method.unlock: return "UNLOCK"; - default: throw (new Error("impossible")); + case caldav.enum_method.report: return "REPORT"; + case caldav.enum_method.mkcalendar: return "MKCALENDAR"; + case caldav.enum_method.acl: return "ACL"; + default: throw (new Error("unhandled method: " + method)); } } caldav.encode_method = encode_method; @@ -13568,6 +13590,9 @@ var lib_plankton; case "MOVE": return caldav.enum_method.move; case "LOCK": return caldav.enum_method.lock; case "UNLOCK": return caldav.enum_method.unlock; + case "REPORT": return caldav.enum_method.report; + case "MKCALENDAR": return caldav.enum_method.mkcalendar; + case "ACL": return caldav.enum_method.acl; default: throw (new Error("unhandled method: " + method_raw)); } } @@ -14240,7 +14265,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, @@ -14276,7 +14301,7 @@ var lib_plankton; // 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, }); @@ -14304,7 +14329,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, } @@ -14396,7 +14421,9 @@ var lib_plankton; ? http_request_body.toString() : null))), "response_body_mimetype": "application/json", - // TODO: no "from"? + /** + * @todo no "from"? + */ "response_body_encode": ((output) => Buffer["from"](JSON.stringify(output))), }, options); const steps = lib_plankton.string.split(path, "/").slice(1); @@ -14440,7 +14467,7 @@ var lib_plankton; // "input_shape": options.input_type, // "output_shape": options.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, @@ -14451,18 +14478,22 @@ var lib_plankton; * @todo check request body mimetype? * @todo check query paramater validity */ - async function call(encode_http_method, 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, } = {}) { - 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": 200, + })), }); // parse target and query parameters // const url_stuff : URL = new URL("http://dummy" + http_request.target); @@ -14480,6 +14511,7 @@ var lib_plankton; // resolve const stuff = routenode_path_read(rest.routetree, steps); const allowed_methods = (Object.keys(stuff.routenode.operations) + .map(x => x.toUpperCase()) .join(", ")); // get version let version; @@ -14510,58 +14542,84 @@ var lib_plankton; } const additional_response_headers = (rest.set_access_control_headers ? { - "Access-Control-Allow-Headers": (([ - "Content-Type", - "X-Api-Key", - ] - .concat((rest.versioning_header_name !== null) - ? [rest.versioning_header_name] - : []) - .concat((rest.authentication.kind === "key_header") - ? [rest.authentication.parameters["name"]] - : [])) - .join(", ")), + /* + "Access-Control-Allow-Headers": ( + ( + [ + "Content-Type", + "X-Api-Key", + ] + .concat( + (rest.versioning_header_name !== null) + ? [rest.versioning_header_name] + : [] + ) + .concat( + (rest.authentication.kind === "key_header") + ? [rest.authentication.parameters["name"]] + : [] + ) + ) + .join(", ") + ), + */ "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": allowed_methods, + // "Access-Control-Allow-Methods": allowed_methods, } : {}); + let response; if (stuff.rest.length > 0) { - return { - "version": "HTTP/1.1", + response = { + "version": http_request.version, "status_code": decode_status_code(404), "headers": {}, "body": null, }; } else { - if (http_request.method === "OPTIONS") { - return { - "version": "HTTP/1.1", - "status_code": decode_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 { + else*/ { const http_method_encoded = encode_http_method(http_request.method).toLowerCase(); if (!(http_method_encoded in stuff.routenode.operations)) { - if (Object.keys(stuff.routenode.operations).length <= 0) { - return { - "version": "HTTP/1.1", - "status_code": decode_status_code(404), + // 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": decode_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 { @@ -14582,18 +14640,15 @@ var lib_plankton; ?? 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, @@ -14601,14 +14656,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": 200, + } + ) ), }, } ); response = { - "version": "HTTP/1.1", + "version": http_request.version, "status_code": 403, "headers": Object.assign( { @@ -14619,36 +14681,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": option_checklevel_restriction, - "checklevel_input": option_checklevel_input, - "checklevel_output": option_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", + "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, @@ -14657,53 +14712,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": 200 + })), }, + "error": ((error === null) + ? + null + : + { + "message": error.toString(), + "file_name": error.fileName, + "line_number": error.lineNumber, + "stack": error.stack, + }), }); response = { - "version": "HTTP/1.1", + "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 = operation.response_body_encode(result.data); response = { - "version": "HTTP/1.1", + "version": http_request.version, "status_code": decode_status_code(result.status_code), - "headers": Object.assign({ - "Content-Type": operation.response_body_mimetype, - }, additional_response_headers), - "body": operation.response_body_encode(result.data), + "headers": Object.assign({}, additional_response_headers, ((body !== null) + ? + { + "Content-Type": operation.response_body_mimetype, + } + : + {}), ((option_set_content_length + && + (body !== null)) + ? + // @ts-ignore + { "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": 200, + })), + }); + return response; } } rest_base.call = call; /** * @see https://swagger.io/specification/#openrest-object */ - function to_oas(http_request_method_to_oas, has_body, 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": { @@ -14711,7 +14795,7 @@ 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": {}, @@ -14783,7 +14867,7 @@ var lib_plankton; })), ]), // query parameters - operation.query_parameters(options.version).map((query_parameter) => ({ + operation.query_parameters(option_version).map((query_parameter) => ({ "name": query_parameter.name, "in": "query", "required": query_parameter.required, @@ -14799,7 +14883,7 @@ var lib_plankton; [ operation.request_body_mimetype, { - "schema": operation.input_schema(options.version), + "schema": operation.input_schema(option_version), } ] ]) @@ -14811,7 +14895,7 @@ var lib_plankton; [ operation.response_body_mimetype, { - "schema": operation.output_schema(options.version), + "schema": operation.output_schema(option_version), } ] ]), @@ -14914,8 +14998,13 @@ var lib_plankton; * @todo check query paramater validity * @todo improve status code mapping */ - async function call(rest, http_request, options = {}) { - return lib_plankton.rest_base.call(lib_plankton.caldav.encode_method, (x => x), rest, http_request, options); + 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.caldav.encode_method, (x => (x === lib_plankton.caldav.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_caldav.call = call; /** @@ -14951,7 +15040,6 @@ var lib_plankton; var server; (function (server) { /** - * @author fenris */ function make(handle, options = {}) { options = Object.assign({ @@ -14969,7 +15057,6 @@ var lib_plankton; } server.make = make; /** - * @author fenris * @deprecated */ function make_old(port, handle) { @@ -14981,7 +15068,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) { @@ -15002,20 +15088,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(); @@ -15029,7 +15116,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; } @@ -15041,21 +15128,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) @@ -15069,12 +15160,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 } }); @@ -15084,7 +15175,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, }); @@ -15094,11 +15185,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 = {})); diff --git a/source/api/actions/caldav_probe.ts b/source/api/actions/caldav_probe.ts index 58b80e2..f6f886d 100644 --- a/source/api/actions/caldav_probe.ts +++ b/source/api/actions/caldav_probe.ts @@ -9,8 +9,8 @@ namespace _zeitbild.api ) : void { register< - any, - any + null, + string >( rest_subject, lib_plankton.caldav.enum_method.propfind, @@ -42,56 +42,82 @@ namespace _zeitbild.api "nullable": false, "type": "string", }), - "response_body_mimetype": "application/xml", + "response_body_mimetype": "text/xml; charset=utf-8", "response_body_encode": output => Buffer.from(output), + // "restriction": restriction_basic_auth, "restriction": restriction_none, /** * @todo examine body */ "execution": async (stuff) => { - return Promise.resolve( - { - "status_code": 207, - "data": ( - /*"\n" - +*/ - lib_plankton.webdav.data_multistatus_encode( - { - "responses": [ + const user_id : (null | type_user_id) = await _zeitbild.api.web_auth( + stuff.headers["Authorization"] + ?? + stuff.headers["authorization"] + ?? + null + ); + if (user_id === null) { + return Promise.resolve( + { + "status_code": 401, + "data": "", + "extra_headers": { + "WWW-Authenticate": "Basic realm=Restricted", + } + } + ); + } + else { + return ( + _zeitbild.service.calendar.overview(user_id) + .then( + (data_raw) => Promise.resolve( + data_raw + .map( + (entry) => ({ + "id": entry.id, + "name": entry.name, + "access_level": _zeitbild.value_object.access_level.to_string(entry.access_level), + }) + ) + ) + ) + .then( + (data) => Promise.resolve({ + "status_code": 207, + "data": ( + "\n" + + + lib_plankton.webdav.data_multistatus_encode( { - "href": "/caldav/events", - "body": { - "propstats": [ - { - "prop": [ - {"name": "d:displayname", "value": "default"}, - // {"name": "cs:getctag", "value": "47"}, // TODO correct value - // {"name": "current-user-privilege-set", "value": ""}, - /* - "uid", - "dtstamp", - "dtstart", - "dtend", - "summary", - "description", - "url", - "location", - */ - ], - "status": "HTTP/2.0 200 OK", - "description": null, + "responses": [ + { + "href": "/caldav/events", + "body": { + "propstats": data.map( + (entry) => ({ + "prop": [ + {"name": "displayname", "value": entry.name}, + // {"name": "cs:getctag", "value": "47"}, // TODO correct value + // {"name": "current-user-privilege-set", "value": ""}, + ], + "status": "HTTP/2.0 200 OK", + "description": entry.access_level, + }) + ), }, - ] - }, + "description": null, + } + ], "description": null, } - ], - "description": null, - } - ) - ), - } - ); + ) + ), + }) + ) + ); + } } } ); diff --git a/source/api/actions/caldav_sniff.ts b/source/api/actions/caldav_sniff.ts new file mode 100644 index 0000000..efee5c9 --- /dev/null +++ b/source/api/actions/caldav_sniff.ts @@ -0,0 +1,46 @@ + +namespace _zeitbild.api +{ + + /** + */ + export function register_caldav_sniff( + rest_subject : lib_plankton.rest_caldav.type_rest + ) : void + { + register< + any, + null + >( + rest_subject, + lib_plankton.caldav.enum_method.options, + "/caldav", + { + "restriction": restriction_none, + "execution": async (stuff) => { + const permitted : boolean = await restriction_basic_auth(stuff); + if (! permitted) { + return Promise.resolve( + { + "status_code": 401, + "data": null, + "extra_headers": { + "WWW-Authenticate": "Basic realm=Restricted", + } + } + ); + } + else { + return Promise.resolve( + { + "status_code": 200, + "data": null + } + ); + } + } + } + ); + } + +} diff --git a/source/api/base.ts b/source/api/base.ts index 4f85f52..8039c23 100644 --- a/source/api/base.ts +++ b/source/api/base.ts @@ -25,6 +25,77 @@ namespace _zeitbild.api } + /** + * @todo outsource? + */ + export async function web_auth( + authorization_string : (null | string) + ) : Promise<(null | _zeitbild.type_user_id)> + { + if (authorization_string === null) { + return Promise.resolve<(null | _zeitbild.type_user_id)>(null); + } + else { + const parts : Array = authorization_string.split(" "); + const strategy : string = parts[0]; + const data_raw : string = parts.slice(1).join(" "); + switch (strategy) { + default: { + lib_plankton.log.notice( + "zeitbild.web_auth.unhandled_strategy", + { + "strategy": strategy, + } + ); + return Promise.resolve<(null | _zeitbild.type_user_id)>(null); + break; + } + case "Basic": { + const data_raw_decoded : string = lib_plankton.base64.decode(data_raw); + const parts_ : Array = data_raw_decoded.split(":"); + const username : string = parts_[0]; + const password_is : string = parts_.slice(1).join(":"); + const {"value": user_id, "error": error} = await lib_plankton.call.try_catch_wrap_async<_zeitbild.type_user_id>( + () => _zeitbild.service.user.identify(username) + ); + if (error !== null) { + lib_plankton.log.notice( + "zeitbild.web_auth.unknown_user", + { + "username": username, + } + ); + return Promise.resolve<(null | _zeitbild.type_user_id)>(null); + } + else { + const password_shall : string = lib_plankton.sha256.get( + username, + _zeitbild.conf.get()["misc"]["auth_salt"] + ); + if (! (password_is === password_shall)) { + /** + * @todo remove + */ + lib_plankton.log.notice( + "zeitbild.web_auth.wrong_pasword", + { + "shall": password_shall, + "is": password_is, + } + ); + return Promise.resolve<(null | _zeitbild.type_user_id)>(null); + } + else { + return Promise.resolve<(null | _zeitbild.type_user_id)>(user_id); + } + } + break; + } + } + } + } + + /** */ export const restriction_logged_in : lib_plankton.rest_caldav.type_restriction = ( @@ -36,6 +107,26 @@ namespace _zeitbild.api ); + /** + */ + export const restriction_basic_auth : lib_plankton.rest_caldav.type_restriction = ( + (stuff) => ( + web_auth( + stuff.headers["Authorization"] + ?? + stuff.headers["authorization"] + ?? + null + ) + .then( + (user_id) => Promise.resolve( + (user_id !== null) + ) + ) + ) + ); + + /** */ export const restriction_none : lib_plankton.rest_caldav.type_restriction = ( diff --git a/source/api/functions.ts b/source/api/functions.ts index 8344792..3b346bb 100644 --- a/source/api/functions.ts +++ b/source/api/functions.ts @@ -52,6 +52,7 @@ namespace _zeitbild.api } // caldav { + _zeitbild.api.register_caldav_sniff(rest_subject); _zeitbild.api.register_caldav_probe(rest_subject); _zeitbild.api.register_caldav_get(rest_subject); } diff --git a/source/conf.ts b/source/conf.ts index ebb56d7..a94484e 100644 --- a/source/conf.ts +++ b/source/conf.ts @@ -41,7 +41,17 @@ namespace _zeitbild.conf "error" ], "default": "info" - } + }, + "format": { + "nullable": false, + "type": "string", + "enum": [ + "human_readable", + "jsonl", + "jsonl_structured", + ], + "default": "human_readable", + }, }, "required": [ ] diff --git a/source/main.ts b/source/main.ts index 9acebe2..72c91ce 100644 --- a/source/main.ts +++ b/source/main.ts @@ -273,11 +273,31 @@ async function main( "kind": "std", "data": { "target": "stdout", - "format": { - "kind": "human_readable", - "data": { + "format": lib_plankton.call.distinguish( + { + "kind": log_output.data.format, + "data": null, + }, + { + "jsonl": () => ({ + "kind": "jsonl", + "data": { + "structured": false, + } + }), + "jsonl_structured": () => ({ + "kind": "jsonl", + "data": { + "structured": true, + } + }), + "human_readable": () => ({ + "kind": "human_readable", + "data": { + } + }), } - } + ), } }, "threshold": log_output.data.threshold, @@ -419,6 +439,7 @@ async function main( "checklevel_restriction": lib_plankton.api.enum_checklevel.hard, // "checklevel_input": lib_plankton.api.enum_checklevel.soft, // "checklevel_output": lib_plankton.api.enum_checklevel.soft, + "set_content_length": false, } ); const output : string = lib_plankton.caldav.encode_response(http_response); @@ -448,6 +469,7 @@ async function main( ) .catch( (error) => { + // console.error(error); process.stderr.write(String(error) + "\n"); } ) diff --git a/source/services/calendar.ts b/source/services/calendar.ts index 8262b68..28686f4 100644 --- a/source/services/calendar.ts +++ b/source/services/calendar.ts @@ -348,7 +348,13 @@ namespace _zeitbild.service.calendar { } ); - const ics_raw : string = http_response.body.toString(); + const ics_raw : string = ( + (http_response.body === null) + ? + "" + : + http_response.body.toString() + ); const vcalendar_list : Array = lib_plankton.ical.ics_decode_multi( ics_raw, { diff --git a/tools/makefile b/tools/makefile index ebedf93..6d5ca33 100644 --- a/tools/makefile +++ b/tools/makefile @@ -76,6 +76,7 @@ ${dir_temp}/zeitbild-unlinked.js: \ ${dir_source}/api/actions/calendar_event_remove.ts \ ${dir_source}/api/actions/events.ts \ ${dir_source}/api/actions/export_ical.ts \ + ${dir_source}/api/actions/caldav_sniff.ts \ ${dir_source}/api/actions/caldav_probe.ts \ ${dir_source}/api/actions/caldav_get.ts \ ${dir_source}/api/functions.ts \ -- 2.47.3 From ef879bb81ba43e4f3f69ceadd1d070aba036acfc Mon Sep 17 00:00:00 2001 From: Fenris Wolf Date: Thu, 28 Nov 2024 23:06:38 +0100 Subject: [PATCH 05/30] [upd] node:xml2js --- lib/node/node_modules/.package-lock.json | 25 + lib/node/node_modules/sax/LICENSE | 41 + lib/node/node_modules/sax/README.md | 228 + lib/node/node_modules/sax/lib/sax.js | 1597 + lib/node/node_modules/sax/package.json | 29 + lib/node/node_modules/xml2js/LICENSE | 19 + lib/node/node_modules/xml2js/README.md | 507 + lib/node/node_modules/xml2js/lib/bom.js | 12 + lib/node/node_modules/xml2js/lib/builder.js | 127 + lib/node/node_modules/xml2js/lib/defaults.js | 72 + lib/node/node_modules/xml2js/lib/parser.js | 395 + .../node_modules/xml2js/lib/processors.js | 34 + lib/node/node_modules/xml2js/lib/xml2js.bc.js | 28337 ++++++++++++++++ lib/node/node_modules/xml2js/lib/xml2js.js | 39 + lib/node/node_modules/xml2js/package.json | 93 + lib/node/node_modules/xmlbuilder/CHANGELOG.md | 470 + lib/node/node_modules/xmlbuilder/LICENSE | 21 + lib/node/node_modules/xmlbuilder/README.md | 86 + lib/node/node_modules/xmlbuilder/appveyor.yml | 20 + .../node_modules/xmlbuilder/lib/Derivation.js | 10 + .../xmlbuilder/lib/DocumentPosition.js | 12 + .../node_modules/xmlbuilder/lib/NodeType.js | 23 + .../xmlbuilder/lib/OperationType.js | 11 + .../node_modules/xmlbuilder/lib/Utility.js | 83 + .../xmlbuilder/lib/WriterState.js | 10 + .../xmlbuilder/lib/XMLAttribute.js | 108 + .../node_modules/xmlbuilder/lib/XMLCData.js | 36 + .../xmlbuilder/lib/XMLCharacterData.js | 79 + .../node_modules/xmlbuilder/lib/XMLComment.js | 36 + .../xmlbuilder/lib/XMLDOMConfiguration.js | 64 + .../xmlbuilder/lib/XMLDOMErrorHandler.js | 16 + .../xmlbuilder/lib/XMLDOMImplementation.js | 32 + .../xmlbuilder/lib/XMLDOMStringList.js | 28 + .../xmlbuilder/lib/XMLDTDAttList.js | 55 + .../xmlbuilder/lib/XMLDTDElement.js | 38 + .../xmlbuilder/lib/XMLDTDEntity.js | 97 + .../xmlbuilder/lib/XMLDTDNotation.js | 52 + .../xmlbuilder/lib/XMLDeclaration.js | 43 + .../node_modules/xmlbuilder/lib/XMLDocType.js | 186 + .../xmlbuilder/lib/XMLDocument.js | 242 + .../xmlbuilder/lib/XMLDocumentCB.js | 528 + .../xmlbuilder/lib/XMLDocumentFragment.js | 24 + .../node_modules/xmlbuilder/lib/XMLDummy.js | 31 + .../node_modules/xmlbuilder/lib/XMLElement.js | 298 + .../xmlbuilder/lib/XMLNamedNodeMap.js | 58 + .../node_modules/xmlbuilder/lib/XMLNode.js | 785 + .../xmlbuilder/lib/XMLNodeFilter.js | 48 + .../xmlbuilder/lib/XMLNodeList.js | 28 + .../lib/XMLProcessingInstruction.js | 49 + .../node_modules/xmlbuilder/lib/XMLRaw.js | 35 + .../xmlbuilder/lib/XMLStreamWriter.js | 176 + .../xmlbuilder/lib/XMLStringWriter.js | 35 + .../xmlbuilder/lib/XMLStringifier.js | 240 + .../node_modules/xmlbuilder/lib/XMLText.js | 69 + .../xmlbuilder/lib/XMLTypeInfo.js | 21 + .../xmlbuilder/lib/XMLUserDataHandler.js | 16 + .../xmlbuilder/lib/XMLWriterBase.js | 428 + lib/node/node_modules/xmlbuilder/lib/index.js | 65 + lib/node/node_modules/xmlbuilder/package.json | 39 + .../xmlbuilder/typings/index.d.ts | 153 + lib/node/package-lock.json | 28 +- lib/node/package.json | 3 +- 62 files changed, 36568 insertions(+), 2 deletions(-) create mode 100644 lib/node/node_modules/sax/LICENSE create mode 100644 lib/node/node_modules/sax/README.md create mode 100644 lib/node/node_modules/sax/lib/sax.js create mode 100644 lib/node/node_modules/sax/package.json create mode 100644 lib/node/node_modules/xml2js/LICENSE create mode 100644 lib/node/node_modules/xml2js/README.md create mode 100644 lib/node/node_modules/xml2js/lib/bom.js create mode 100644 lib/node/node_modules/xml2js/lib/builder.js create mode 100644 lib/node/node_modules/xml2js/lib/defaults.js create mode 100644 lib/node/node_modules/xml2js/lib/parser.js create mode 100644 lib/node/node_modules/xml2js/lib/processors.js create mode 100644 lib/node/node_modules/xml2js/lib/xml2js.bc.js create mode 100644 lib/node/node_modules/xml2js/lib/xml2js.js create mode 100644 lib/node/node_modules/xml2js/package.json create mode 100644 lib/node/node_modules/xmlbuilder/CHANGELOG.md create mode 100644 lib/node/node_modules/xmlbuilder/LICENSE create mode 100644 lib/node/node_modules/xmlbuilder/README.md create mode 100644 lib/node/node_modules/xmlbuilder/appveyor.yml create mode 100644 lib/node/node_modules/xmlbuilder/lib/Derivation.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/DocumentPosition.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/NodeType.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/OperationType.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/Utility.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/WriterState.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLAttribute.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLCData.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLCharacterData.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLComment.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDOMConfiguration.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDOMErrorHandler.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDOMImplementation.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDOMStringList.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDTDAttList.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDTDElement.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDTDEntity.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDTDNotation.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDeclaration.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDocType.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDocument.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDocumentCB.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDocumentFragment.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLDummy.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLElement.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLNamedNodeMap.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLNode.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLNodeFilter.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLNodeList.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLProcessingInstruction.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLRaw.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLStreamWriter.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLStringWriter.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLStringifier.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLText.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLTypeInfo.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLUserDataHandler.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/XMLWriterBase.js create mode 100644 lib/node/node_modules/xmlbuilder/lib/index.js create mode 100644 lib/node/node_modules/xmlbuilder/package.json create mode 100644 lib/node/node_modules/xmlbuilder/typings/index.d.ts diff --git a/lib/node/node_modules/.package-lock.json b/lib/node/node_modules/.package-lock.json index a2de0a2..ef3e572 100644 --- a/lib/node/node_modules/.package-lock.json +++ b/lib/node/node_modules/.package-lock.json @@ -1245,6 +1245,11 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "optional": true }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -1577,6 +1582,26 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/lib/node/node_modules/sax/LICENSE b/lib/node/node_modules/sax/LICENSE new file mode 100644 index 0000000..1ccceb0 --- /dev/null +++ b/lib/node/node_modules/sax/LICENSE @@ -0,0 +1,41 @@ +The ISC License + +Copyright (c) 2010-2024 Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +==== + +`String.fromCodePoint` by Mathias Bynens used according to terms of MIT +License, as follows: + +Copyright (c) 2010-2024 Mathias Bynens + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/node/node_modules/sax/README.md b/lib/node/node_modules/sax/README.md new file mode 100644 index 0000000..c974b0f --- /dev/null +++ b/lib/node/node_modules/sax/README.md @@ -0,0 +1,228 @@ +# sax js + +A sax-style parser for XML and HTML. + +Designed with [node](http://nodejs.org/) in mind, but should work fine in +the browser or other CommonJS implementations. + +## What This Is + +* A very simple tool to parse through an XML string. +* A stepping stone to a streaming HTML parser. +* A handy way to deal with RSS and other mostly-ok-but-kinda-broken XML + docs. + +## What This Is (probably) Not + +* An HTML Parser - That's a fine goal, but this isn't it. It's just + XML. +* A DOM Builder - You can use it to build an object model out of XML, + but it doesn't do that out of the box. +* XSLT - No DOM = no querying. +* 100% Compliant with (some other SAX implementation) - Most SAX + implementations are in Java and do a lot more than this does. +* An XML Validator - It does a little validation when in strict mode, but + not much. +* A Schema-Aware XSD Thing - Schemas are an exercise in fetishistic + masochism. +* A DTD-aware Thing - Fetching DTDs is a much bigger job. + +## Regarding `Hello, world!').close(); + +// stream usage +// takes the same options as the parser +var saxStream = require("sax").createStream(strict, options) +saxStream.on("error", function (e) { + // unhandled errors will throw, since this is a proper node + // event emitter. + console.error("error!", e) + // clear the error + this._parser.error = null + this._parser.resume() +}) +saxStream.on("opentag", function (node) { + // same object as above +}) +// pipe is supported, and it's readable/writable +// same chunks coming in also go out. +fs.createReadStream("file.xml") + .pipe(saxStream) + .pipe(fs.createWriteStream("file-copy.xml")) +``` + + +## Arguments + +Pass the following arguments to the parser function. All are optional. + +`strict` - Boolean. Whether or not to be a jerk. Default: `false`. + +`opt` - Object bag of settings regarding string formatting. All default to `false`. + +Settings supported: + +* `trim` - Boolean. Whether or not to trim text and comment nodes. +* `normalize` - Boolean. If true, then turn any whitespace into a single + space. +* `lowercase` - Boolean. If true, then lowercase tag names and attribute names + in loose mode, rather than uppercasing them. +* `xmlns` - Boolean. If true, then namespaces are supported. +* `position` - Boolean. If false, then don't track line/col/position. +* `strictEntities` - Boolean. If true, only parse [predefined XML + entities](http://www.w3.org/TR/REC-xml/#sec-predefined-ent) + (`&`, `'`, `>`, `<`, and `"`) +* `unquotedAttributeValues` - Boolean. If true, then unquoted + attribute values are allowed. Defaults to `false` when `strict` + is true, `true` otherwise. + +## Methods + +`write` - Write bytes onto the stream. You don't have to do this all at +once. You can keep writing as much as you want. + +`close` - Close the stream. Once closed, no more data may be written until +it is done processing the buffer, which is signaled by the `end` event. + +`resume` - To gracefully handle errors, assign a listener to the `error` +event. Then, when the error is taken care of, you can call `resume` to +continue parsing. Otherwise, the parser will not continue while in an error +state. + +## Members + +At all times, the parser object will have the following members: + +`line`, `column`, `position` - Indications of the position in the XML +document where the parser currently is looking. + +`startTagPosition` - Indicates the position where the current tag starts. + +`closed` - Boolean indicating whether or not the parser can be written to. +If it's `true`, then wait for the `ready` event to write again. + +`strict` - Boolean indicating whether or not the parser is a jerk. + +`opt` - Any options passed into the constructor. + +`tag` - The current tag being dealt with. + +And a bunch of other stuff that you probably shouldn't touch. + +## Events + +All events emit with a single argument. To listen to an event, assign a +function to `on`. Functions get executed in the this-context of +the parser object. The list of supported events are also in the exported +`EVENTS` array. + +When using the stream interface, assign handlers using the EventEmitter +`on` function in the normal fashion. + +`error` - Indication that something bad happened. The error will be hanging +out on `parser.error`, and must be deleted before parsing can continue. By +listening to this event, you can keep an eye on that kind of stuff. Note: +this happens *much* more in strict mode. Argument: instance of `Error`. + +`text` - Text node. Argument: string of text. + +`doctype` - The ``. Argument: +object with `name` and `body` members. Attributes are not parsed, as +processing instructions have implementation dependent semantics. + +`sgmldeclaration` - Random SGML declarations. Stuff like `` +would trigger this kind of event. This is a weird thing to support, so it +might go away at some point. SAX isn't intended to be used to parse SGML, +after all. + +`opentagstart` - Emitted immediately when the tag name is available, +but before any attributes are encountered. Argument: object with a +`name` field and an empty `attributes` set. Note that this is the +same object that will later be emitted in the `opentag` event. + +`opentag` - An opening tag. Argument: object with `name` and `attributes`. +In non-strict mode, tag names are uppercased, unless the `lowercase` +option is set. If the `xmlns` option is set, then it will contain +namespace binding information on the `ns` member, and will have a +`local`, `prefix`, and `uri` member. + +`closetag` - A closing tag. In loose mode, tags are auto-closed if their +parent closes. In strict mode, well-formedness is enforced. Note that +self-closing tags will have `closeTag` emitted immediately after `openTag`. +Argument: tag name. + +`attribute` - An attribute node. Argument: object with `name` and `value`. +In non-strict mode, attribute names are uppercased, unless the `lowercase` +option is set. If the `xmlns` option is set, it will also contains namespace +information. + +`comment` - A comment node. Argument: the string of the comment. + +`opencdata` - The opening tag of a ``) of a `` tags trigger a `"script"` +event, and their contents are not checked for special xml characters. +If you pass `noscript: true`, then this behavior is suppressed. + +## Reporting Problems + +It's best to write a failing test if you find an issue. I will always +accept pull requests with failing tests if they demonstrate intended +behavior, but it is very hard to figure out what issue you're describing +without a test. Writing a test is also the best way for you yourself +to figure out if you really understand the issue you think you have with +sax-js. diff --git a/lib/node/node_modules/sax/lib/sax.js b/lib/node/node_modules/sax/lib/sax.js new file mode 100644 index 0000000..122ad8e --- /dev/null +++ b/lib/node/node_modules/sax/lib/sax.js @@ -0,0 +1,1597 @@ +;(function (sax) { // wrapper for non-node envs + sax.parser = function (strict, opt) { return new SAXParser(strict, opt) } + sax.SAXParser = SAXParser + sax.SAXStream = SAXStream + sax.createStream = createStream + + // When we pass the MAX_BUFFER_LENGTH position, start checking for buffer overruns. + // When we check, schedule the next check for MAX_BUFFER_LENGTH - (max(buffer lengths)), + // since that's the earliest that a buffer overrun could occur. This way, checks are + // as rare as required, but as often as necessary to ensure never crossing this bound. + // Furthermore, buffers are only tested at most once per write(), so passing a very + // large string into write() might have undesirable effects, but this is manageable by + // the caller, so it is assumed to be safe. Thus, a call to write() may, in the extreme + // edge case, result in creating at most one complete copy of the string passed in. + // Set to Infinity to have unlimited buffers. + sax.MAX_BUFFER_LENGTH = 64 * 1024 + + var buffers = [ + 'comment', 'sgmlDecl', 'textNode', 'tagName', 'doctype', + 'procInstName', 'procInstBody', 'entity', 'attribName', + 'attribValue', 'cdata', 'script' + ] + + sax.EVENTS = [ + 'text', + 'processinginstruction', + 'sgmldeclaration', + 'doctype', + 'comment', + 'opentagstart', + 'attribute', + 'opentag', + 'closetag', + 'opencdata', + 'cdata', + 'closecdata', + 'error', + 'end', + 'ready', + 'script', + 'opennamespace', + 'closenamespace' + ] + + function SAXParser (strict, opt) { + if (!(this instanceof SAXParser)) { + return new SAXParser(strict, opt) + } + + var parser = this + clearBuffers(parser) + parser.q = parser.c = '' + parser.bufferCheckPosition = sax.MAX_BUFFER_LENGTH + parser.opt = opt || {} + parser.opt.lowercase = parser.opt.lowercase || parser.opt.lowercasetags + parser.looseCase = parser.opt.lowercase ? 'toLowerCase' : 'toUpperCase' + parser.tags = [] + parser.closed = parser.closedRoot = parser.sawRoot = false + parser.tag = parser.error = null + parser.strict = !!strict + parser.noscript = !!(strict || parser.opt.noscript) + parser.state = S.BEGIN + parser.strictEntities = parser.opt.strictEntities + parser.ENTITIES = parser.strictEntities ? Object.create(sax.XML_ENTITIES) : Object.create(sax.ENTITIES) + parser.attribList = [] + + // namespaces form a prototype chain. + // it always points at the current tag, + // which protos to its parent tag. + if (parser.opt.xmlns) { + parser.ns = Object.create(rootNS) + } + + // disallow unquoted attribute values if not otherwise configured + // and strict mode is true + if (parser.opt.unquotedAttributeValues === undefined) { + parser.opt.unquotedAttributeValues = !strict; + } + + // mostly just for error reporting + parser.trackPosition = parser.opt.position !== false + if (parser.trackPosition) { + parser.position = parser.line = parser.column = 0 + } + emit(parser, 'onready') + } + + if (!Object.create) { + Object.create = function (o) { + function F () {} + F.prototype = o + var newf = new F() + return newf + } + } + + if (!Object.keys) { + Object.keys = function (o) { + var a = [] + for (var i in o) if (o.hasOwnProperty(i)) a.push(i) + return a + } + } + + function checkBufferLength (parser) { + var maxAllowed = Math.max(sax.MAX_BUFFER_LENGTH, 10) + var maxActual = 0 + for (var i = 0, l = buffers.length; i < l; i++) { + var len = parser[buffers[i]].length + if (len > maxAllowed) { + // Text/cdata nodes can get big, and since they're buffered, + // we can get here under normal conditions. + // Avoid issues by emitting the text node now, + // so at least it won't get any bigger. + switch (buffers[i]) { + case 'textNode': + closeText(parser) + break + + case 'cdata': + emitNode(parser, 'oncdata', parser.cdata) + parser.cdata = '' + break + + case 'script': + emitNode(parser, 'onscript', parser.script) + parser.script = '' + break + + default: + error(parser, 'Max buffer length exceeded: ' + buffers[i]) + } + } + maxActual = Math.max(maxActual, len) + } + // schedule the next check for the earliest possible buffer overrun. + var m = sax.MAX_BUFFER_LENGTH - maxActual + parser.bufferCheckPosition = m + parser.position + } + + function clearBuffers (parser) { + for (var i = 0, l = buffers.length; i < l; i++) { + parser[buffers[i]] = '' + } + } + + function flushBuffers (parser) { + closeText(parser) + if (parser.cdata !== '') { + emitNode(parser, 'oncdata', parser.cdata) + parser.cdata = '' + } + if (parser.script !== '') { + emitNode(parser, 'onscript', parser.script) + parser.script = '' + } + } + + SAXParser.prototype = { + end: function () { end(this) }, + write: write, + resume: function () { this.error = null; return this }, + close: function () { return this.write(null) }, + flush: function () { flushBuffers(this) } + } + + var Stream + try { + Stream = require('stream').Stream + } catch (ex) { + Stream = function () {} + } + if (!Stream) Stream = function () {} + + var streamWraps = sax.EVENTS.filter(function (ev) { + return ev !== 'error' && ev !== 'end' + }) + + function createStream (strict, opt) { + return new SAXStream(strict, opt) + } + + function SAXStream (strict, opt) { + if (!(this instanceof SAXStream)) { + return new SAXStream(strict, opt) + } + + Stream.apply(this) + + this._parser = new SAXParser(strict, opt) + this.writable = true + this.readable = true + + var me = this + + this._parser.onend = function () { + me.emit('end') + } + + this._parser.onerror = function (er) { + me.emit('error', er) + + // if didn't throw, then means error was handled. + // go ahead and clear error, so we can write again. + me._parser.error = null + } + + this._decoder = null + + streamWraps.forEach(function (ev) { + Object.defineProperty(me, 'on' + ev, { + get: function () { + return me._parser['on' + ev] + }, + set: function (h) { + if (!h) { + me.removeAllListeners(ev) + me._parser['on' + ev] = h + return h + } + me.on(ev, h) + }, + enumerable: true, + configurable: false + }) + }) + } + + SAXStream.prototype = Object.create(Stream.prototype, { + constructor: { + value: SAXStream + } + }) + + SAXStream.prototype.write = function (data) { + if (typeof Buffer === 'function' && + typeof Buffer.isBuffer === 'function' && + Buffer.isBuffer(data)) { + if (!this._decoder) { + var SD = require('string_decoder').StringDecoder + this._decoder = new SD('utf8') + } + data = this._decoder.write(data) + } + + this._parser.write(data.toString()) + this.emit('data', data) + return true + } + + SAXStream.prototype.end = function (chunk) { + if (chunk && chunk.length) { + this.write(chunk) + } + this._parser.end() + return true + } + + SAXStream.prototype.on = function (ev, handler) { + var me = this + if (!me._parser['on' + ev] && streamWraps.indexOf(ev) !== -1) { + me._parser['on' + ev] = function () { + var args = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments) + args.splice(0, 0, ev) + me.emit.apply(me, args) + } + } + + return Stream.prototype.on.call(me, ev, handler) + } + + // this really needs to be replaced with character classes. + // XML allows all manner of ridiculous numbers and digits. + var CDATA = '[CDATA[' + var DOCTYPE = 'DOCTYPE' + var XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace' + var XMLNS_NAMESPACE = 'http://www.w3.org/2000/xmlns/' + var rootNS = { xml: XML_NAMESPACE, xmlns: XMLNS_NAMESPACE } + + // http://www.w3.org/TR/REC-xml/#NT-NameStartChar + // This implementation works on strings, a single character at a time + // as such, it cannot ever support astral-plane characters (10000-EFFFF) + // without a significant breaking change to either this parser, or the + // JavaScript language. Implementation of an emoji-capable xml parser + // is left as an exercise for the reader. + var nameStart = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/ + + var nameBody = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040.\d-]/ + + var entityStart = /[#:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/ + var entityBody = /[#:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040.\d-]/ + + function isWhitespace (c) { + return c === ' ' || c === '\n' || c === '\r' || c === '\t' + } + + function isQuote (c) { + return c === '"' || c === '\'' + } + + function isAttribEnd (c) { + return c === '>' || isWhitespace(c) + } + + function isMatch (regex, c) { + return regex.test(c) + } + + function notMatch (regex, c) { + return !isMatch(regex, c) + } + + var S = 0 + sax.STATE = { + BEGIN: S++, // leading byte order mark or whitespace + BEGIN_WHITESPACE: S++, // leading whitespace + TEXT: S++, // general stuff + TEXT_ENTITY: S++, // & and such. + OPEN_WAKA: S++, // < + SGML_DECL: S++, // + SCRIPT: S++, //