From 553ced9038654ef661796db7cf49fb61bb946b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Fra=C3=9F?= Date: Mon, 27 May 2024 17:32:42 +0200 Subject: [PATCH] [mod] strikte Typisierung --- source/api/actions/member_modify.ts | 31 ++- .../actions/member_password_change_execute.ts | 41 +-- .../member_password_change_initialize.ts | 15 +- source/api/actions/member_project.ts | 27 +- source/api/actions/member_register.ts | 49 ++-- source/api/actions/member_summon.ts | 32 ++- source/api/actions/session_begin.ts | 39 ++- source/api/base.ts | 15 +- source/conf.ts | 14 +- source/database.ts | 6 +- source/helpers.ts | 35 ++- source/repositories/name_index.ts | 2 +- source/services/member.ts | 237 ++++++++++-------- tools/makefile | 2 +- 14 files changed, 331 insertions(+), 214 deletions(-) diff --git a/source/api/actions/member_modify.ts b/source/api/actions/member_modify.ts index ae4ace6..3314766 100644 --- a/source/api/actions/member_modify.ts +++ b/source/api/actions/member_modify.ts @@ -64,19 +64,24 @@ namespace _espe.api }), "restriction": restriction_logged_in, "execution": async ({"path_parameters": path_parameters, "input": input}) => { - const member_id : _espe.type.member_id = parseInt(path_parameters["id"]); - await _espe.service.member.modify( - member_id, - { - "email_address_private": input.email_address_private, - "registered": input.registered, - "enabled": input.enabled, - } - ); - return Promise.resolve({ - "status_code": 200, - "data": null - }); + if (input === null) { + return Promise.reject(new Error("impossible")); + } + else { + const member_id : _espe.type.member_id = parseInt(path_parameters["id"]); + await _espe.service.member.modify( + member_id, + { + "email_address_private": input.email_address_private, + "registered": input.registered, + "enabled": input.enabled, + } + ); + return Promise.resolve({ + "status_code": 200, + "data": null + }); + } } } ); diff --git a/source/api/actions/member_password_change_execute.ts b/source/api/actions/member_password_change_execute.ts index 03236a2..5d9d86e 100644 --- a/source/api/actions/member_password_change_execute.ts +++ b/source/api/actions/member_password_change_execute.ts @@ -90,24 +90,29 @@ namespace _espe.api }), "restriction": restriction_none, "execution": ({"path_parameters": path_parameters, "input": input}) => { - const member_id : _espe.type.member_id = parseInt(path_parameters["id"]); - return ( - _espe.service.member.password_change_execute( - member_id, - input.token, - input.password_new - ) - .then( - flaws => Promise.resolve({ - "status_code": ( - (flaws.length <= 0) - ? 200 - : 409 - ), - "data": flaws - }) - ) - ); + if (input === null) { + return Promise.reject(new Error("impossible")); + } + else { + const member_id : _espe.type.member_id = parseInt(path_parameters["id"]); + return ( + _espe.service.member.password_change_execute( + member_id, + input.token, + input.password_new + ) + .then( + flaws => Promise.resolve({ + "status_code": ( + (flaws.length <= 0) + ? 200 + : 409 + ), + "data": flaws + }) + ) + ); + } }, } ) diff --git a/source/api/actions/member_password_change_initialize.ts b/source/api/actions/member_password_change_initialize.ts index 1be19b0..b333ba7 100644 --- a/source/api/actions/member_password_change_initialize.ts +++ b/source/api/actions/member_password_change_initialize.ts @@ -62,11 +62,16 @@ namespace _espe.api }), "restriction": restriction_none, "execution": async ({"input": input}) => { - await _espe.service.member.password_change_initialize(input.identifier, input.url_template); - return Promise.resolve({ - "status_code": 200, - "data": null - }); + if (input === null) { + return Promise.reject(new Error("impossible")); + } + else { + await _espe.service.member.password_change_initialize(input.identifier, input.url_template); + return Promise.resolve({ + "status_code": 200, + "data": null + }); + } }, } ) diff --git a/source/api/actions/member_project.ts b/source/api/actions/member_project.ts index 9ebeaff..f188cec 100644 --- a/source/api/actions/member_project.ts +++ b/source/api/actions/member_project.ts @@ -67,17 +67,22 @@ namespace _espe.api }), "restriction": restriction_logged_in, "execution": async ({"input": input}) => { - const member_id : _espe.type.member_id = await _espe.service.member.project( - { - "membership_number": input.membership_number, - "name_real_value": input.name_real_value, - "email_address_private": (input.email_address_private ?? null), - } - ); - return Promise.resolve({ - "status_code": 201, - "data": member_id - }); + if (input === null) { + return Promise.reject(new Error("impossible")); + } + else { + const member_id : _espe.type.member_id = await _espe.service.member.project( + { + "membership_number": input.membership_number, + "name_real_value": input.name_real_value, + "email_address_private": (input.email_address_private ?? null), + } + ); + return Promise.resolve({ + "status_code": 201, + "data": member_id + }); + } } } ); diff --git a/source/api/actions/member_register.ts b/source/api/actions/member_register.ts index fb114cb..f4fc4b0 100644 --- a/source/api/actions/member_register.ts +++ b/source/api/actions/member_register.ts @@ -115,28 +115,33 @@ namespace _espe.api stuff => stuff.query_parameters["key"] ), "execution": ({"path_parameters": path_parameters, "input": input}) => { - const member_id : _espe.type.member_id = parseInt(path_parameters["id"]); - return ( - _espe.service.member.register( - member_id, - { - "email_use_veiled_address": input.email_use_veiled_address, - "email_use_nominal_address": input.email_use_nominal_address, - "email_redirect_to_private_address": input.email_redirect_to_private_address, - "password": input.password, - } - ) - .then( - flaws => Promise.resolve({ - "status_code": ( - (flaws.length <= 0) - ? 200 - : 409 - ), - "data": flaws - }) - ) - ); + if (input === null) { + return Promise.reject(new Error("impossible")); + } + else { + const member_id : _espe.type.member_id = parseInt(path_parameters["id"]); + return ( + _espe.service.member.register( + member_id, + { + "email_use_veiled_address": input.email_use_veiled_address, + "email_use_nominal_address": input.email_use_nominal_address, + "email_redirect_to_private_address": input.email_redirect_to_private_address, + "password": input.password, + } + ) + .then( + flaws => Promise.resolve({ + "status_code": ( + (flaws.length <= 0) + ? 200 + : 409 + ), + "data": flaws + }) + ) + ); + } }, } ) diff --git a/source/api/actions/member_summon.ts b/source/api/actions/member_summon.ts index 98594db..0cbf719 100644 --- a/source/api/actions/member_summon.ts +++ b/source/api/actions/member_summon.ts @@ -26,7 +26,11 @@ namespace _espe.api { url_template : string; }, - string + ( + null + | + string + ) >( rest_subject, lib_plankton.http.enum_method.post, @@ -35,13 +39,25 @@ namespace _espe.api "description": "sendet an ein Mitglied eine E-Mail mit Aufforderung zur Registrierung", "restriction": restriction_logged_in, "execution": async ({"path_parameters": path_parameters, "input": input}) => { - const member_id : _espe.type.member_id = parseInt(path_parameters["id"]); - const url : string = await _espe.service.member.summon(member_id, input.url_template); - - return Promise.resolve({ - "status_code": 200, - "data": url, - }); + if (input === null) { + return Promise.reject(new Error("impossible")); + } + else { + const member_id : _espe.type.member_id = parseInt(path_parameters["id"]); + const url : (null | string) = await _espe.service.member.summon(member_id, input.url_template); + + return ( + (url === null) + ? Promise.resolve({ + "status_code": 409, + "data": null, + }) + : Promise.resolve({ + "status_code": 200, + "data": url, + }) + ); + } } } ); diff --git a/source/api/actions/session_begin.ts b/source/api/actions/session_begin.ts index 2c12681..392a319 100644 --- a/source/api/actions/session_begin.ts +++ b/source/api/actions/session_begin.ts @@ -22,7 +22,17 @@ namespace _espe.api rest_subject : lib_plankton.rest.type_rest ) : void { - lib_plankton.rest.register<{name : string; password : string;}, string>( + lib_plankton.rest.register< + { + name : string; + password : string; + }, + ( + null + | + string + ) + >( rest_subject, lib_plankton.http.enum_method.post, _espe.conf.get().server.path_base + "/session/begin", @@ -50,19 +60,24 @@ namespace _espe.api }), "restriction": restriction_none, "execution": async ({"input": input}) => { - const admin : (null | _espe.service.admin.type_value) = await _espe.service.admin.login(input.name, input.password); - if (admin === null) { - return Promise.resolve({ - "status_code": 403, - "data": null, - }); + if (input === null) { + return Promise.reject(new Error("impossible")); } else { - const session_key : string = await lib_plankton.session.begin(admin.name); - return Promise.resolve({ - "status_code": 201, - "data": session_key, - }); + const admin : (null | _espe.service.admin.type_value) = await _espe.service.admin.login(input.name, input.password); + if (admin === null) { + return Promise.resolve({ + "status_code": 403, + "data": null, + }); + } + else { + const session_key : string = await lib_plankton.session.begin(admin.name); + return Promise.resolve({ + "status_code": 201, + "data": session_key, + }); + } } }, } diff --git a/source/api/base.ts b/source/api/base.ts index dc9e6c0..c06e34b 100644 --- a/source/api/base.ts +++ b/source/api/base.ts @@ -16,6 +16,17 @@ You should have received a copy of the GNU General Public License along with thi namespace _espe.api { + /** + * @todo zu plankton auslagern? + */ + type type_stuff = { + version: (null | string); + headers: Record; + path_parameters: Record; + query_parameters: Record; + }; + + /** */ export async function session_from_stuff( @@ -69,8 +80,8 @@ namespace _espe.api /** */ export function restriction_verification( - extract_data : ((stuff) => any), - extract_verification : ((stuff) => string) + extract_data : ((stuff : type_stuff) => any), + extract_verification : ((stuff : type_stuff) => string) ) : lib_plankton.rest.type_restriction { return ( diff --git a/source/conf.ts b/source/conf.ts index aa6a8d6..54fa333 100644 --- a/source/conf.ts +++ b/source/conf.ts @@ -16,16 +16,6 @@ You should have received a copy of the GNU General Public License along with thi namespace _espe.conf { - /** - */ - type type_smtp_credentials = { - host : string; - port : int; - username : string; - password : string; - }; - - /** */ export type type_conf = { @@ -72,7 +62,7 @@ namespace _espe.conf { kind : "regular"; data : { - smtp_credentials : type_smtp_credentials; + smtp_credentials : _espe.helpers.type_smtp_credentials; sender : string; }; } @@ -80,7 +70,7 @@ namespace _espe.conf { kind : "redirect"; data : { - smtp_credentials : type_smtp_credentials; + smtp_credentials : _espe.helpers.type_smtp_credentials; sender : string; target : string; }; diff --git a/source/database.ts b/source/database.ts index 032246d..ee68858 100644 --- a/source/database.ts +++ b/source/database.ts @@ -30,9 +30,13 @@ namespace _espe.database { switch (_espe.conf.get().database.kind) { case "sqlite": { + type type_parameters = { + path : string; + }; + const parameters : type_parameters = (_espe.conf.get().database.data as type_parameters); return lib_plankton.database.sqlite_database( { - "path": _espe.conf.get().database.data["path"], + "path": parameters.path, "verbose": false, } ); diff --git a/source/helpers.ts b/source/helpers.ts index 38c3daf..2280ec7 100644 --- a/source/helpers.ts +++ b/source/helpers.ts @@ -16,6 +16,16 @@ You should have received a copy of the GNU General Public License along with thi namespace _espe.helpers { + /** + */ + export type type_smtp_credentials = { + host : string; + port : int; + username : string; + password : string; + }; + + /** */ /* @@ -188,30 +198,41 @@ namespace _espe.helpers ); switch (mode) { case "regular": { - if (_espe.conf.get().email_sending.data["smtp_credentials"] === null) { + type type_parameters = { + smtp_credentials : type_smtp_credentials; + sender : string; + }; + const parameters : type_parameters = (_espe.conf.get().email_sending.data as type_parameters); + if (parameters.smtp_credentials === null) { return Promise.reject("no smtp credentials specified; add in conf as 'email_sending.data.smtp_credentials'!"); } else { return email_send_real( - _espe.conf.get().email_sending.data["smtp_credentials"], + parameters.smtp_credentials, receivers, subject, content, { - "sender": _espe.conf.get().email_sending.data["sender"], + "sender": parameters.sender, } ); } break; } case "redirect": { - if (_espe.conf.get().email_sending.data["smtp_credentials"] === null) { + type type_parameters = { + smtp_credentials : type_smtp_credentials; + sender : string; + target : string; + }; + const parameters : type_parameters = (_espe.conf.get().email_sending.data as type_parameters); + if (parameters.smtp_credentials === null) { return Promise.reject("no smtp credentials specified; add in conf as 'email_sending.data.smtp_credentials'!"); } else { return email_send_real( - _espe.conf.get().email_sending.data["smtp_credentials"], - [_espe.conf.get().email_sending.data["target"]], + parameters.smtp_credentials, + [parameters.target], lib_plankton.string.coin( "[REDIRECTION] {{receivers}} | {{original_subject}}", { @@ -221,7 +242,7 @@ namespace _espe.helpers ), content, { - "sender": _espe.conf.get().email_sending.data["sender"], + "sender": parameters.sender, } ); } diff --git a/source/repositories/name_index.ts b/source/repositories/name_index.ts index 922dfcf..a3afda9 100644 --- a/source/repositories/name_index.ts +++ b/source/repositories/name_index.ts @@ -69,7 +69,7 @@ namespace _espe.repository.name_index ? name : await lib_plankton.sha256.get( lib_plankton.json.encode(name), - _espe.conf.get().settings.name_index.salt + (_espe.conf.get().settings.name_index.salt ?? undefined) ) ); } diff --git a/source/services/member.ts b/source/services/member.ts index f80fbcc..9c5bec1 100644 --- a/source/services/member.ts +++ b/source/services/member.ts @@ -52,38 +52,45 @@ namespace _espe.service.member ) : Array<{incident : string; details : Record}> { let flaws : Array<{incident : string; details : Record}> = []; + const settings : { + minimum_length : (null | int); + maximum_length : (null | int); + must_contain_letter : boolean; + must_contain_number : boolean; + must_contain_special_character : boolean; + } = _espe.conf.get().settings.password_policy; if ( - (_espe.conf.get().settings.password_policy.minimum_length !== null) + (settings.minimum_length !== null) && - (password.length < _espe.conf.get().settings.password_policy.minimum_length) + (password.length < settings.minimum_length) ) { flaws.push( { "incident": "too_short", "details": { - "minimum_length": _espe.conf.get().settings.password_policy.minimum_length, + "minimum_length": settings.minimum_length, "actual_length": password.length, } } ); } if ( - (_espe.conf.get().settings.password_policy.maximum_length !== null) + (settings.maximum_length !== null) && - (password.length > _espe.conf.get().settings.password_policy.maximum_length) + (password.length > settings.maximum_length) ) { flaws.push( { "incident": "too_long", "details": { - "maximum_length": _espe.conf.get().settings.password_policy.maximum_length, + "maximum_length": settings.maximum_length, "actual_length": password.length, } } ); } if ( - _espe.conf.get().settings.password_policy.must_contain_letter + settings.must_contain_letter && (! (new RegExp("[a-zA-Z]")).test(password)) ) { @@ -96,7 +103,7 @@ namespace _espe.service.member ); } if ( - _espe.conf.get().settings.password_policy.must_contain_number + settings.must_contain_number && (! (new RegExp("[0-9]")).test(password)) ) { @@ -109,7 +116,7 @@ namespace _espe.service.member ); } if ( - _espe.conf.get().settings.password_policy.must_contain_special_character + settings.must_contain_special_character && (! (new RegExp("[!?-_.,;/\~%&$'()\\[\\]{}^'#|+*<>=\"`:@]")).test(password)) ) { @@ -138,13 +145,13 @@ namespace _espe.service.member "object": lib_plankton.call.convey( object.name_real_value, [ - x => x.toLowerCase(), - x => x.replace(new RegExp(" ", "g"), "."), - x => x.replace(new RegExp("[äÄ]", "g"), "ae"), - x => x.replace(new RegExp("[öÖ]", "g"), "oe"), - x => x.replace(new RegExp("[üÜ]", "g"), "ue"), - x => x.replace(new RegExp("[ß]", "g"), "ss"), - x => x.replace(new RegExp("[^a-z-\.]", "g"), "_"), + (x : string) => x.toLowerCase(), + (x : string) => x.replace(new RegExp(" ", "g"), "."), + (x : string) => x.replace(new RegExp("[äÄ]", "g"), "ae"), + (x : string) => x.replace(new RegExp("[öÖ]", "g"), "oe"), + (x : string) => x.replace(new RegExp("[üÜ]", "g"), "ue"), + (x : string) => x.replace(new RegExp("[ß]", "g"), "ss"), + (x : string) => x.replace(new RegExp("[^a-z-\.]", "g"), "_"), ] ), "extension": ( @@ -227,7 +234,7 @@ namespace _espe.service.member */ function password_image( password : (null | string) - ) : Promise + ) : Promise<(null | string)> { return ( ( @@ -236,7 +243,7 @@ namespace _espe.service.member (! (password === "")) ) ? _espe.helpers.bcrypt_compute(password) - : null + : Promise.resolve<(null | string)>(null) ); } @@ -332,31 +339,36 @@ namespace _espe.service.member export async function summon( member_id : _espe.type.member_id, url_template : string - ) : Promise + ) : Promise<(null | string)> { const member_object : _espe.type.member_object = await get(member_id); - const verification : string = await _espe.helpers.verification_get(member_id); - - const url : string = lib_plankton.string.coin( - url_template, - { - "verification": verification, - } - ); - await _espe.helpers.email_send( - [ - member_object.email_address_private, - ], - _espe.conf.get().settings.registration_email.subject, - lib_plankton.string.coin( - _espe.conf.get().settings.registration_email.body, + if (member_object.email_address_private === null) { + return null; + } + else { + const verification : string = await _espe.helpers.verification_get(member_id); + + const url : string = lib_plankton.string.coin( + url_template, { - "name": member_object.name_real_value, - "url": url, + "verification": verification, } - ) - ); - return url; + ); + await _espe.helpers.email_send( + [ + member_object.email_address_private, + ], + _espe.conf.get().settings.registration_email.subject, + lib_plankton.string.coin( + _espe.conf.get().settings.registration_email.body, + { + "name": member_object.name_real_value, + "url": url, + } + ) + ); + return url; + } } @@ -406,7 +418,7 @@ namespace _espe.service.member email_use_veiled_address : boolean; email_use_nominal_address : boolean; email_redirect_to_private_address : boolean; - password : string; + password : (null | string); }, options : { notify_admins ?: boolean; @@ -431,7 +443,11 @@ namespace _espe.service.member flaws.push({"incident": "already_registered", "details": {}}); } else { - if (password_set) { + if ( + password_set + && + (data.password !== null) + ) { flaws = flaws.concat( validate_password(data.password) .map(flaw => ({"incident": ("password_" + flaw.incident), "details": flaw.details})) @@ -461,9 +477,11 @@ namespace _espe.service.member else { await _espe.helpers.email_send( ( - _espe.conf.get().admins - .map(admin => admin.email_address) - .filter(x => (x !== null)) + ( + _espe.conf.get().admins + .map(admin => admin.email_address) + .filter(x => (x !== null)) + ) as Array ), "Eingegangene Registrierung", // TODO ("Member-ID: " + member_id.toFixed(0)) @@ -518,7 +536,8 @@ namespace _espe.service.member url_template : string ) : Promise { - if (_espe.conf.get().settings.frontend_url_base === null) { + const frontend_url_base : (null | string) = _espe.conf.get().settings.frontend_url_base; + if (frontend_url_base === null) { return Promise.reject(new Error("no frontend url base set; add in conf as 'settings.frontend_url_base'!")); } else { @@ -607,7 +626,7 @@ namespace _espe.service.member "url": lib_plankton.string.coin( "{{base}}{{rest}}", { - "base": _espe.conf.get().settings.frontend_url_base, + "base": frontend_url_base, "rest": lib_plankton.string.coin( url_template, { @@ -639,62 +658,67 @@ namespace _espe.service.member ) : Promise;}>> { const member_object_old : _espe.type.member_object = await _espe.repository.member.read(member_id); - let flaws : Array<{incident : string; details : Record;}> = []; - if ( - (member_object_old.password_change_token === null) - || - (! (token === member_object_old.password_change_token)) - ) { - lib_plankton.log.notice( - "member_password_change_token_invalid", - { - "member_id": member_id, - "token_sent": token, - } - ); - flaws.push({"incident": "token_invalid", "details": {}}); + if (member_object_old.email_address_private === null) { + return Promise.reject(new Error("private e-mail address missing")); } else { - flaws = flaws.concat( - validate_password(password_new) - .map(flaw => ({"incident": ("password_" + flaw.incident), "details": flaw.details})) - ); - if (flaws.length > 0) { - // do nothing + let flaws : Array<{incident : string; details : Record;}> = []; + if ( + (member_object_old.password_change_token === null) + || + (! (token === member_object_old.password_change_token)) + ) { + lib_plankton.log.notice( + "member_password_change_token_invalid", + { + "member_id": member_id, + "token_sent": token, + } + ); + flaws.push({"incident": "token_invalid", "details": {}}); } else { - const member_object_new : _espe.type.member_object = { - "membership_number": member_object_old.membership_number, - "name_real_value": member_object_old.name_real_value, - "name_real_index": member_object_old.name_real_index, - "email_address_private": member_object_old.email_address_private, - "registered": member_object_old.registered, - "enabled": member_object_old.enabled, - "email_use_veiled_address": member_object_old.email_use_veiled_address, - "email_use_nominal_address": member_object_old.email_use_nominal_address, - "email_redirect_to_private_address": member_object_old.email_redirect_to_private_address, - "email_allow_sending": member_object_old.email_allow_sending, - "password_image": await password_image(password_new), - "password_change_last_attempt": member_object_old.password_change_last_attempt, - "password_change_token": null, - }; - await _espe.repository.member.update(member_id, member_object_new); - notify_change(); - await _espe.helpers.email_send( - [ - member_object_old.email_address_private, - ], - _espe.conf.get().settings.password_change.execution_email.subject, - lib_plankton.string.coin( - _espe.conf.get().settings.password_change.execution_email.body, - { - "name": name_display(member_object_old), - } - ) + flaws = flaws.concat( + validate_password(password_new) + .map(flaw => ({"incident": ("password_" + flaw.incident), "details": flaw.details})) ); + if (flaws.length > 0) { + // do nothing + } + else { + const member_object_new : _espe.type.member_object = { + "membership_number": member_object_old.membership_number, + "name_real_value": member_object_old.name_real_value, + "name_real_index": member_object_old.name_real_index, + "email_address_private": member_object_old.email_address_private, + "registered": member_object_old.registered, + "enabled": member_object_old.enabled, + "email_use_veiled_address": member_object_old.email_use_veiled_address, + "email_use_nominal_address": member_object_old.email_use_nominal_address, + "email_redirect_to_private_address": member_object_old.email_redirect_to_private_address, + "email_allow_sending": member_object_old.email_allow_sending, + "password_image": await password_image(password_new), + "password_change_last_attempt": member_object_old.password_change_last_attempt, + "password_change_token": null, + }; + await _espe.repository.member.update(member_id, member_object_new); + notify_change(); + await _espe.helpers.email_send( + [ + member_object_old.email_address_private, + ], + _espe.conf.get().settings.password_change.execution_email.subject, + lib_plankton.string.coin( + _espe.conf.get().settings.password_change.execution_email.body, + { + "name": name_display(member_object_old), + } + ) + ); + } } + return flaws; } - return flaws; } @@ -727,20 +751,31 @@ namespace _espe.service.member const nm_yaml = require("yaml"); + type type_entry = { + disabled : boolean; + displayname : string; + email : string; + groups : Array; + password : string; + }; return lib_plankton.call.convey( ( - (options.custom_data !== null) + ( + (options.custom_data !== undefined) + && + (options.custom_data !== null) + ) ? (options.custom_data.map((member_object, index) => ({"id": index, "object": member_object}))) : await dump() ), [ - x => x.map( + (x : Array) => x.map( entry => Object.assign( entry, {"email_address": email_address(entry.object)} ) ), - x => x.filter( + (x : Array) => x.filter( entry => ( entry.object.registered && @@ -753,7 +788,7 @@ namespace _espe.service.member (entry.email_address !== null) ) ), - x => x.map( + (x : Array) => x.map( entry => ([ name_login(entry.object), { @@ -766,8 +801,8 @@ namespace _espe.service.member ]) ), Object.fromEntries, - x => ({"users": x}), - x => nm_yaml.stringify(x), + (x : Record) => ({"users": x}), + (x : {users : Record}) => nm_yaml.stringify(x), ] ); } diff --git a/tools/makefile b/tools/makefile index 250395a..05e594f 100644 --- a/tools/makefile +++ b/tools/makefile @@ -67,7 +67,7 @@ ${dir_temp}/espe-core.js ${dir_temp}/espe-core.d.ts: \ ${dir_source}/conf.ts @ ${cmd_log} "compile | core …" @ ${cmd_mkdir} $(dir $@) - @ tsc --lib es2020 $^ --outFile ${dir_temp}/espe-core.js --declaration + @ tsc --lib es2020 --strict $^ --outFile ${dir_temp}/espe-core.js --declaration .PHONY: main main: core ${dir_build}/espe