diff --git a/.gitignore b/.gitignore index e964244..3d1a868 100644 --- a/.gitignore +++ b/.gitignore @@ -299,3 +299,6 @@ TSWLatexianTemp* # option is specified. Footnotes are the stored in a file with suffix Notes.bib. # Uncomment the next line to have this generated file ignored. #*Notes.bib + +# draw.io +*.drawio.bkp diff --git a/build/slides.bcf-SAVE-ERROR b/build/slides.bcf-SAVE-ERROR deleted file mode 100644 index 820c979..0000000 --- a/build/slides.bcf-SAVE-ERROR +++ /dev/null @@ -1,2377 +0,0 @@ - - - - - - output_encoding - utf8 - - - input_encoding - utf8 - - - debug - 0 - - - mincrossrefs - 2 - - - minxrefs - 2 - - - sortcase - 1 - - - sortupper - 1 - - - - - - - alphaothers - + - - - extradatecontext - labelname - labeltitle - - - labelalpha - 0 - - - labelnamespec - shortauthor - author - shorteditor - editor - translator - - - labeltitle - 0 - - - labeltitlespec - shorttitle - title - maintitle - - - labeltitleyear - 0 - - - labeldateparts - 0 - - - labeldatespec - date - year - eventdate - origdate - urldate - nodate - - - julian - 0 - - - gregorianstart - 1582-10-15 - - - maxalphanames - 3 - - - maxbibnames - 3 - - - maxcitenames - 3 - - - maxsortnames - 3 - - - maxitems - 3 - - - minalphanames - 1 - - - minbibnames - 1 - - - mincitenames - 1 - - - minsortnames - 1 - - - minitems - 1 - - - nohashothers - 0 - - - noroman - 0 - - - nosortothers - 0 - - - pluralothers - 0 - - - singletitle - 0 - - - skipbib - 0 - - - skipbiblist - 0 - - - skiplab - 0 - - - sortalphaothers - + - - - sortlocale - french - - - sortingtemplatename - nty - - - sortsets - 0 - - - uniquelist - false - - - uniquename - false - - - uniqueprimaryauthor - 0 - - - uniquetitle - 0 - - - uniquebaretitle - 0 - - - uniquework - 0 - - - useprefix - 0 - - - useafterword - 1 - - - useannotator - 1 - - - useauthor - 1 - - - usebookauthor - 1 - - - usecommentator - 1 - - - useeditor - 1 - - - useeditora - 1 - - - useeditorb - 1 - - - useeditorc - 1 - - - useforeword - 1 - - - useholder - 1 - - - useintroduction - 1 - - - usenamea - 1 - - - usenameb - 1 - - - usenamec - 1 - - - usetranslator - 0 - - - useshortauthor - 1 - - - useshorteditor - 1 - - - - - - extradatecontext - labelname - labeltitle - - - labelalpha - 0 - - - labelnamespec - shortauthor - author - shorteditor - editor - translator - - - labeltitle - 0 - - - labeltitlespec - shorttitle - title - maintitle - - - labeltitleyear - 0 - - - labeldateparts - 0 - - - labeldatespec - date - year - eventdate - origdate - urldate - nodate - - - maxalphanames - 3 - - - maxbibnames - 3 - - - maxcitenames - 3 - - - maxsortnames - 3 - - - maxitems - 3 - - - minalphanames - 1 - - - minbibnames - 1 - - - mincitenames - 1 - - - minsortnames - 1 - - - minitems - 1 - - - nohashothers - 0 - - - noroman - 0 - - - nosortothers - 0 - - - singletitle - 0 - - - skipbib - 0 - - - skipbiblist - 0 - - - skiplab - 0 - - - uniquelist - false - - - uniquename - false - - - uniqueprimaryauthor - 0 - - - uniquetitle - 0 - - - uniquebaretitle - 0 - - - uniquework - 0 - - - useprefix - 0 - - - useafterword - 1 - - - useannotator - 1 - - - useauthor - 1 - - - usebookauthor - 1 - - - usecommentator - 1 - - - useeditor - 1 - - - useeditora - 1 - - - useeditorb - 1 - - - useeditorc - 1 - - - useforeword - 1 - - - useholder - 1 - - - useintroduction - 1 - - - usenamea - 1 - - - usenameb - 1 - - - usenamec - 1 - - - usetranslator - 0 - - - useshortauthor - 1 - - - useshorteditor - 1 - - - - - datamodel - labelalphanametemplate - labelalphatemplate - inheritance - translit - uniquenametemplate - namehashtemplate - sortingnamekeytemplate - sortingtemplate - extradatespec - extradatecontext - labelnamespec - labeltitlespec - labeldatespec - controlversion - alphaothers - sortalphaothers - presort - citepagerange - texencoding - bibencoding - sortingtemplatename - sortlocale - language - autolang - langhook - indexing - hyperref - backrefsetstyle - block - pagetracker - citecounter - citetracker - ibidtracker - idemtracker - opcittracker - loccittracker - labeldate - labeltime - dateera - date - time - eventdate - eventtime - origdate - origtime - urldate - urltime - alldatesusetime - alldates - alltimes - gregorianstart - autocite - notetype - uniquelist - uniquename - refsection - refsegment - citereset - sortlos - babel - datelabel - backrefstyle - arxiv - familyinits - giveninits - prefixinits - suffixinits - useafterword - useannotator - useauthor - usebookauthor - usecommentator - useeditor - useeditora - useeditorb - useeditorc - useforeword - useholder - useintroduction - usenamea - usenameb - usenamec - usetranslator - useshortauthor - useshorteditor - debug - loadfiles - safeinputenc - sortcase - sortupper - terseinits - abbreviate - dateabbrev - clearlang - sortcites - sortsets - backref - backreffloats - trackfloats - parentracker - labeldateusetime - datecirca - dateuncertain - dateusetime - eventdateusetime - origdateusetime - urldateusetime - julian - datezeros - timezeros - timezones - seconds - autopunct - punctfont - labelnumber - labelalpha - labeltitle - labeltitleyear - labeldateparts - pluralothers - nohashothers - nosortothers - noroman - singletitle - uniquetitle - uniquebaretitle - uniquework - uniqueprimaryauthor - defernumbers - locallabelwidth - bibwarn - useprefix - skipbib - skipbiblist - skiplab - dataonly - defernums - firstinits - sortfirstinits - sortgiveninits - labelyear - isbn - url - doi - eprint - related - subentry - bibtexcaseprotection - mincrossrefs - minxrefs - maxnames - minnames - maxbibnames - minbibnames - maxcitenames - mincitenames - maxsortnames - minsortnames - maxitems - minitems - maxalphanames - minalphanames - maxparens - dateeraauto - - - alphaothers - sortalphaothers - presort - indexing - citetracker - ibidtracker - idemtracker - opcittracker - loccittracker - uniquelist - uniquename - familyinits - giveninits - prefixinits - suffixinits - useafterword - useannotator - useauthor - usebookauthor - usecommentator - useeditor - useeditora - useeditorb - useeditorc - useforeword - useholder - useintroduction - usenamea - usenameb - usenamec - usetranslator - useshortauthor - useshorteditor - terseinits - abbreviate - dateabbrev - clearlang - labelnumber - labelalpha - labeltitle - labeltitleyear - labeldateparts - nohashothers - nosortothers - noroman - singletitle - uniquetitle - uniquebaretitle - uniquework - uniqueprimaryauthor - useprefix - skipbib - skipbiblist - skiplab - dataonly - skiplos - labelyear - isbn - url - doi - eprint - related - subentry - bibtexcaseprotection - labelalphatemplate - translit - sortexclusion - sortinclusion - extradatecontext - labelnamespec - labeltitlespec - labeldatespec - maxnames - minnames - maxbibnames - minbibnames - maxcitenames - mincitenames - maxsortnames - minsortnames - maxitems - minitems - maxalphanames - minalphanames - - - noinherit - nametemplates - labelalphanametemplatename - uniquenametemplatename - namehashtemplatename - sortingnamekeytemplatename - presort - indexing - citetracker - ibidtracker - idemtracker - opcittracker - loccittracker - uniquelist - uniquename - familyinits - giveninits - prefixinits - suffixinits - useafterword - useannotator - useauthor - usebookauthor - usecommentator - useeditor - useeditora - useeditorb - useeditorc - useforeword - useholder - useintroduction - usenamea - usenameb - usenamec - usetranslator - useshortauthor - useshorteditor - terseinits - abbreviate - dateabbrev - clearlang - labelnumber - labelalpha - labeltitle - labeltitleyear - labeldateparts - nohashothers - nosortothers - noroman - singletitle - uniquetitle - uniquebaretitle - uniquework - uniqueprimaryauthor - useprefix - skipbib - skipbiblist - skiplab - dataonly - skiplos - isbn - url - doi - eprint - related - subentry - bibtexcaseprotection - maxnames - minnames - maxbibnames - minbibnames - maxcitenames - mincitenames - maxsortnames - minsortnames - maxitems - minitems - maxalphanames - minalphanames - - - nametemplates - labelalphanametemplatename - uniquenametemplatename - namehashtemplatename - sortingnamekeytemplatename - uniquelist - uniquename - familyinits - giveninits - prefixinits - suffixinits - terseinits - nohashothers - nosortothers - useprefix - - - nametemplates - labelalphanametemplatename - uniquenametemplatename - namehashtemplatename - sortingnamekeytemplatename - uniquename - familyinits - giveninits - prefixinits - suffixinits - terseinits - useprefix - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - prefix - family - - - - - shorthand - label - labelname - labelname - - - year - - - - - - labelyear - year - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - prefix - family - given - - - - family - given - prefix - suffix - - - - - prefix - family - - - given - - - suffix - - - prefix - - - mm - - - - sf,sm,sn,pf,pm,pn,pp - family,given,prefix,suffix - boolean,integer,string,xml - default,transliteration,transcription,translation - - - article - artwork - audio - bibnote - book - bookinbook - booklet - collection - commentary - customa - customb - customc - customd - custome - customf - dataset - inbook - incollection - inproceedings - inreference - image - jurisdiction - legal - legislation - letter - manual - misc - movie - music - mvcollection - mvreference - mvproceedings - mvbook - online - patent - performance - periodical - proceedings - reference - report - review - set - software - standard - suppbook - suppcollection - suppperiodical - thesis - unpublished - video - xdata - - - sortyear - volume - volumes - abstract - addendum - annotation - booksubtitle - booktitle - booktitleaddon - chapter - edition - eid - entrysubtype - eprintclass - eprinttype - eventtitle - eventtitleaddon - gender - howpublished - indexsorttitle - indextitle - isan - isbn - ismn - isrn - issn - issue - issuesubtitle - issuetitle - issuetitleaddon - iswc - journalsubtitle - journaltitle - journaltitleaddon - label - langid - langidopts - library - mainsubtitle - maintitle - maintitleaddon - nameaddon - note - number - origtitle - pagetotal - part - relatedstring - relatedtype - reprinttitle - series - shorthandintro - subtitle - title - titleaddon - usera - userb - userc - userd - usere - userf - venue - version - shorthand - shortjournal - shortseries - shorttitle - sorttitle - sortshorthand - sortkey - presort - institution - lista - listb - listc - listd - liste - listf - location - organization - origlocation - origpublisher - publisher - afterword - annotator - author - bookauthor - commentator - editor - editora - editorb - editorc - foreword - holder - introduction - namea - nameb - namec - translator - shortauthor - shorteditor - sortname - authortype - editoratype - editorbtype - editorctype - editortype - bookpagination - nameatype - namebtype - namectype - pagination - pubstate - type - language - origlanguage - crossref - xref - date - endyear - year - month - day - hour - minute - second - timezone - yeardivision - endmonth - endday - endhour - endminute - endsecond - endtimezone - endyeardivision - eventdate - eventendyear - eventyear - eventmonth - eventday - eventhour - eventminute - eventsecond - eventtimezone - eventyeardivision - eventendmonth - eventendday - eventendhour - eventendminute - eventendsecond - eventendtimezone - eventendyeardivision - origdate - origendyear - origyear - origmonth - origday - orighour - origminute - origsecond - origtimezone - origyeardivision - origendmonth - origendday - origendhour - origendminute - origendsecond - origendtimezone - origendyeardivision - urldate - urlendyear - urlyear - urlmonth - urlday - urlhour - urlminute - urlsecond - urltimezone - urlyeardivision - urlendmonth - urlendday - urlendhour - urlendminute - urlendsecond - urlendtimezone - urlendyeardivision - doi - eprint - file - verba - verbb - verbc - url - xdata - ids - entryset - related - keywords - options - relatedoptions - pages - execute - - - abstract - annotation - authortype - bookpagination - crossref - day - doi - eprint - eprintclass - eprinttype - endday - endhour - endminute - endmonth - endsecond - endtimezone - endyear - endyeardivision - entryset - entrysubtype - execute - file - gender - hour - ids - indextitle - indexsorttitle - isan - ismn - iswc - keywords - label - langid - langidopts - library - lista - listb - listc - listd - liste - listf - minute - month - namea - nameb - namec - nameatype - namebtype - namectype - nameaddon - options - origday - origendday - origendhour - origendminute - origendmonth - origendsecond - origendtimezone - origendyear - origendyeardivision - orighour - origminute - origmonth - origsecond - origtimezone - origyear - origyeardivision - origlocation - origpublisher - origtitle - pagination - presort - related - relatedoptions - relatedstring - relatedtype - second - shortauthor - shorteditor - shorthand - shorthandintro - shortjournal - shortseries - shorttitle - sortkey - sortname - sortshorthand - sorttitle - sortyear - timezone - url - urlday - urlendday - urlendhour - urlendminute - urlendmonth - urlendsecond - urlendtimezone - urlendyear - urlhour - urlminute - urlmonth - urlsecond - urltimezone - urlyear - usera - userb - userc - userd - usere - userf - verba - verbb - verbc - xdata - xref - year - yeardivision - - - set - entryset - - - article - addendum - annotator - author - commentator - editor - editora - editorb - editorc - editortype - editoratype - editorbtype - editorctype - eid - issn - issue - issuetitle - issuesubtitle - issuetitleaddon - journalsubtitle - journaltitle - journaltitleaddon - language - note - number - origlanguage - pages - pubstate - series - subtitle - title - titleaddon - translator - version - volume - - - bibnote - note - - - book - author - addendum - afterword - annotator - chapter - commentator - edition - editor - editora - editorb - editorc - editortype - editoratype - editorbtype - editorctype - eid - foreword - introduction - isbn - language - location - maintitle - maintitleaddon - mainsubtitle - note - number - origlanguage - pages - pagetotal - part - publisher - pubstate - series - subtitle - title - titleaddon - translator - volume - volumes - - - mvbook - addendum - afterword - annotator - author - commentator - edition - editor - editora - editorb - editorc - editortype - editoratype - editorbtype - editorctype - foreword - introduction - isbn - language - location - note - number - origlanguage - pagetotal - publisher - pubstate - series - subtitle - title - titleaddon - translator - volume - volumes - - - inbook - bookinbook - suppbook - addendum - afterword - annotator - author - booktitle - bookauthor - booksubtitle - booktitleaddon - chapter - commentator - edition - editor - editora - editorb - editorc - editortype - editoratype - editorbtype - editorctype - eid - foreword - introduction - isbn - language - location - mainsubtitle - maintitle - maintitleaddon - note - number - origlanguage - part - publisher - pages - pubstate - series - subtitle - title - titleaddon - translator - volume - volumes - - - booklet - addendum - author - chapter - editor - editortype - eid - howpublished - language - location - note - pages - pagetotal - pubstate - subtitle - title - titleaddon - type - - - collection - reference - addendum - afterword - annotator - chapter - commentator - edition - editor - editora - editorb - editorc - editortype - editoratype - editorbtype - editorctype - eid - foreword - introduction - isbn - language - location - mainsubtitle - maintitle - maintitleaddon - note - number - origlanguage - pages - pagetotal - part - publisher - pubstate - series - subtitle - title - titleaddon - translator - volume - volumes - - - mvcollection - mvreference - addendum - afterword - annotator - author - commentator - edition - editor - editora - editorb - editorc - editortype - editoratype - editorbtype - editorctype - foreword - introduction - isbn - language - location - note - number - origlanguage - publisher - pubstate - subtitle - title - titleaddon - translator - volume - volumes - - - incollection - suppcollection - inreference - addendum - afterword - annotator - author - booksubtitle - booktitle - booktitleaddon - chapter - commentator - edition - editor - editora - editorb - editorc - editortype - editoratype - editorbtype - editorctype - eid - foreword - introduction - isbn - language - location - mainsubtitle - maintitle - maintitleaddon - note - number - origlanguage - pages - part - publisher - pubstate - series - subtitle - title - titleaddon - translator - volume - volumes - - - dataset - addendum - author - edition - editor - editortype - language - location - note - number - organization - publisher - pubstate - series - subtitle - title - titleaddon - type - version - - - manual - addendum - author - chapter - edition - editor - editortype - eid - isbn - language - location - note - number - organization - pages - pagetotal - publisher - pubstate - series - subtitle - title - titleaddon - type - version - - - misc - software - addendum - author - editor - editortype - howpublished - language - location - note - organization - pubstate - subtitle - title - titleaddon - type - version - - - online - addendum - author - editor - editortype - language - note - organization - pubstate - subtitle - title - titleaddon - version - - - patent - addendum - author - holder - location - note - number - pubstate - subtitle - title - titleaddon - type - version - - - periodical - addendum - editor - editora - editorb - editorc - editortype - editoratype - editorbtype - editorctype - issn - issue - issuesubtitle - issuetitle - issuetitleaddon - language - note - number - pubstate - series - subtitle - title - titleaddon - volume - yeardivision - - - mvproceedings - addendum - editor - editortype - eventday - eventendday - eventendhour - eventendminute - eventendmonth - eventendsecond - eventendtimezone - eventendyear - eventendyeardivision - eventhour - eventminute - eventmonth - eventsecond - eventtimezone - eventyear - eventyeardivision - eventtitle - eventtitleaddon - isbn - language - location - note - number - organization - pagetotal - publisher - pubstate - series - subtitle - title - titleaddon - venue - volumes - - - proceedings - addendum - chapter - editor - editortype - eid - eventday - eventendday - eventendhour - eventendminute - eventendmonth - eventendsecond - eventendtimezone - eventendyear - eventendyeardivision - eventhour - eventminute - eventmonth - eventsecond - eventtimezone - eventyear - eventyeardivision - eventtitle - eventtitleaddon - isbn - language - location - mainsubtitle - maintitle - maintitleaddon - note - number - organization - pages - pagetotal - part - publisher - pubstate - series - subtitle - title - titleaddon - venue - volume - volumes - - - inproceedings - addendum - author - booksubtitle - booktitle - booktitleaddon - chapter - editor - editortype - eid - eventday - eventendday - eventendhour - eventendminute - eventendmonth - eventendsecond - eventendtimezone - eventendyear - eventendyeardivision - eventhour - eventminute - eventmonth - eventsecond - eventtimezone - eventyear - eventyeardivision - eventtitle - eventtitleaddon - isbn - language - location - mainsubtitle - maintitle - maintitleaddon - note - number - organization - pages - part - publisher - pubstate - series - subtitle - title - titleaddon - venue - volume - volumes - - - report - addendum - author - chapter - eid - institution - isrn - language - location - note - number - pages - pagetotal - pubstate - subtitle - title - titleaddon - type - version - - - thesis - addendum - author - chapter - eid - institution - language - location - note - pages - pagetotal - pubstate - subtitle - title - titleaddon - type - - - unpublished - addendum - author - eventday - eventendday - eventendhour - eventendminute - eventendmonth - eventendsecond - eventendtimezone - eventendyear - eventendyeardivision - eventhour - eventminute - eventmonth - eventsecond - eventtimezone - eventyear - eventyeardivision - eventtitle - eventtitleaddon - howpublished - language - location - note - pubstate - subtitle - title - titleaddon - type - venue - - - abstract - addendum - afterword - annotator - author - bookauthor - booksubtitle - booktitle - booktitleaddon - chapter - commentator - editor - editora - editorb - editorc - foreword - holder - institution - introduction - issuesubtitle - issuetitle - issuetitleaddon - journalsubtitle - journaltitle - journaltitleaddon - location - mainsubtitle - maintitle - maintitleaddon - nameaddon - note - organization - origlanguage - origlocation - origpublisher - origtitle - part - publisher - relatedstring - series - shortauthor - shorteditor - shorthand - shortjournal - shortseries - shorttitle - sortname - sortshorthand - sorttitle - subtitle - title - titleaddon - translator - venue - - - article - book - inbook - bookinbook - suppbook - booklet - collection - incollection - suppcollection - manual - misc - mvbook - mvcollection - online - patent - periodical - suppperiodical - proceedings - inproceedings - reference - inreference - report - set - thesis - unpublished - - - date - year - - - - - set - - entryset - - - - article - - author - journaltitle - title - - - - book - mvbook - - author - title - - - - inbook - bookinbook - suppbook - - author - title - booktitle - - - - booklet - - - author - editor - - title - - - - collection - reference - mvcollection - mvreference - - editor - title - - - - incollection - suppcollection - inreference - - author - editor - title - booktitle - - - - dataset - - title - - - - manual - - title - - - - misc - software - - title - - - - online - - title - - url - doi - eprint - - - - - patent - - author - title - number - - - - periodical - - editor - title - - - - proceedings - mvproceedings - - title - - - - inproceedings - - author - title - booktitle - - - - report - - author - title - type - institution - - - - thesis - - author - title - type - institution - - - - unpublished - - author - title - - - - - isbn - - - issn - - - ismn - - - gender - - - - - - - slides.bib - - - HandbookTypeScriptHandbook - poggialiConciseTypeScriptBook2026 diff --git a/build/slides.pdf b/build/slides.pdf index 5191394..6b10796 100644 Binary files a/build/slides.pdf and b/build/slides.pdf differ diff --git a/img/architecture.png b/img/architecture.png new file mode 100644 index 0000000..77cae17 Binary files /dev/null and b/img/architecture.png differ diff --git a/img/async.png b/img/async.png new file mode 100644 index 0000000..d749241 Binary files /dev/null and b/img/async.png differ diff --git a/img/rest-wikipedia.png b/img/rest-wikipedia.png new file mode 100644 index 0000000..052d716 Binary files /dev/null and b/img/rest-wikipedia.png differ diff --git a/img/roy_fielding_OSCON_2008.jpg b/img/roy_fielding_OSCON_2008.jpg new file mode 100644 index 0000000..48132d7 Binary files /dev/null and b/img/roy_fielding_OSCON_2008.jpg differ diff --git a/img/screenshot.png b/img/screenshot.png new file mode 100644 index 0000000..1ad8d23 Binary files /dev/null and b/img/screenshot.png differ diff --git a/img/type-guard-1.png b/img/type-guard-1.png new file mode 100644 index 0000000..d5f5b6a Binary files /dev/null and b/img/type-guard-1.png differ diff --git a/img/type-guard-2.png b/img/type-guard-2.png new file mode 100644 index 0000000..c8e5cef Binary files /dev/null and b/img/type-guard-2.png differ diff --git a/img/type-guard-3.png b/img/type-guard-3.png new file mode 100644 index 0000000..4e825b2 Binary files /dev/null and b/img/type-guard-3.png differ diff --git a/img/type-guard-4.png b/img/type-guard-4.png new file mode 100644 index 0000000..8687170 Binary files /dev/null and b/img/type-guard-4.png differ diff --git a/img/type-guard-5.png b/img/type-guard-5.png new file mode 100644 index 0000000..fec5b5d Binary files /dev/null and b/img/type-guard-5.png differ diff --git a/res/.$architecture.drawio.bkp b/res/.$architecture.drawio.bkp deleted file mode 100644 index 898ec35..0000000 --- a/res/.$architecture.drawio.bkp +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/.$js-event-loop.drawio.bkp b/res/.$js-event-loop.drawio.bkp deleted file mode 100644 index 877e3c2..0000000 --- a/res/.$js-event-loop.drawio.bkp +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/architecture.drawio b/res/architecture.drawio index 5cdcdf3..44b3ada 100644 --- a/res/architecture.drawio +++ b/res/architecture.drawio @@ -1,35 +1,41 @@ - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + + + + + + + diff --git a/res/async.drawio b/res/async.drawio new file mode 100644 index 0000000..0dcb7d7 --- /dev/null +++ b/res/async.drawio @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/type-guard.drawio b/res/type-guard.drawio new file mode 100644 index 0000000..3e4b741 --- /dev/null +++ b/res/type-guard.drawio @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/slides.bib b/slides.bib index 7711c61..ca3db06 100644 --- a/slides.bib +++ b/slides.bib @@ -2,14 +2,16 @@ title = {The {{Concise TypeScript Book}}}, author = {Poggiali, Simone}, year = 2026, - howpublished = {https://gibbok.github.io/typescript-book/book/the-concise-typescript-book/}, + url = {https://gibbok.github.io/typescript-book/book/the-concise-typescript-book/}, + urldate = {2026-01-19} } @book{HandbookTypeScriptHandbook, title = {The {{TypeScript Handbook}}}, author = {Microsoft}, year = 2026, - howpublished = {https://www.typescriptlang.org/docs/handbook/intro.html}, + url = {https://www.typescriptlang.org/docs/handbook/intro.html}, + urldate = {2026-01-19} } @article{greggFlameGraph2016, @@ -23,7 +25,52 @@ pages = {48--57}, issn = {0001-0782, 1557-7317}, doi = {10.1145/2909476}, - urldate = {2026-01-16}, abstract = {This visualization of software execution is a new necessity for performance profiling and debugging.}, langid = {english}, } + +@misc{fowlerMicroservices, + title = {Microservices}, + author = {Lewis, James and Fowler, Martin}, + journal = {martinfowler.com}, + abstract = {Defining the microservices architectural style by describing their nine common characteristics}, + url = {https://martinfowler.com/articles/microservices.html}, + urldate = {2026-01-19} +} + +@misc{ParseDontValidate2019, + author = {King, Alexis}, + title = {Parse, Don’t Validate}, + year = {2019}, + month = nov, + url = {https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/}, + urldate = {2026-01-27} +} + +@book{kleppmannDesigningDataIntensiveApplications2017, + author = {Kleppmann, Martin}, + title = {Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems}, + publisher = {O'Reilly Media}, + address = {Sebastopol, CA}, + year = {2017}, + edition = {1st}, + isbn = {978-1449373320}, + pages = {xix + 590} +} + +@book{sharvitDataOrientedProgramming2022, + author = {Sharvit, Yehonathan}, + title = {Data-Oriented Programming: Reduce Software Complexity}, + publisher = {Manning Publications}, + address = {Shelter Island, NY}, + year = {2022}, + isbn = {978-1617298578}, + pages = {325} +} + +@book{fielding2000architectural, + title={Architectural styles and the design of network-based software architectures}, + author={Fielding, Roy Thomas}, + year={2000}, + publisher={University of California, Irvine} +} diff --git a/slides.pdf b/slides.pdf index 5191394..6b10796 100644 Binary files a/slides.pdf and b/slides.pdf differ diff --git a/slides.tex b/slides.tex index 8e9a993..c3e99da 100644 --- a/slides.tex +++ b/slides.tex @@ -44,19 +44,19 @@ \begin{frame}{Introduction} \begin{center} - \url{https://info-demo-sor.univ-brest.fr} + \url{http://info-demo-sor.univ-brest.fr:3000} \end{center} \end{frame} -\begin{framefont}{\small} +\begin{framefont}{\footnotesize} \begin{frame}[t]{Plan du cours} \begin{columns} - \column{0.5\textwidth} + \column{0.6\textwidth} \tableofcontents%[hideallsubsections] - \column{0.5\textwidth} + \column{0.4\textwidth} \begin{center} - \includegraphics[width=0.9\columnwidth]{img/logos.png} + \includegraphics[width=\columnwidth]{img/screenshot.png} \end{center} \end{columns} \end{frame} @@ -69,7 +69,7 @@ \item Objectifs : \begin{itemize} \item Développer une application suivant l'\textbf{architecture trois tiers}, s'appuyant sur des communications via \textbf{HTTP} et \textbf{WebSockets} ; - \item Comprendre les mécanismes pour l'\textbf{authentification} (avec ou sans état) d'un client auprès d'un serveur ; + \item Comprendre les mécanismes de l'\textbf{authentification} (avec ou sans état) d'un client auprès d'un serveur ; \item S'initier au \textbf{déploiement} d'une application répartie à l'aide d'un \textit{reverse proxy}. \end{itemize} \item Prérequis : @@ -167,7 +167,7 @@ \end{itemize} \end{frame} -\section{Cas d'usage} +\section{Cas d'étude applicatif} \begin{frame}{Présentation de l'application} \begin{block}{Description} @@ -210,20 +210,26 @@ \section{Architecture : modèle client/serveur} \begin{frame}{Vue d'ensemble} - + \centering + \includegraphics[width=.9\textwidth]{img/architecture.png} \end{frame} \begin{frame}{Monolithes ou microservices} \centering \includegraphics[width=.8\textwidth]{img/scaling-monolith.png} + + \addtocounter{footnote}{1} + \footnotetext{\fullcite{fowlerMicroservices}} \end{frame} \begin{frame}{Monolithes ou microservices} \centering \includegraphics[width=.8\textwidth]{img/scaling-uservices.png} + + \footnotetext{\fullcite{fowlerMicroservices}} \end{frame} -\section{JavaScript, TypeScript et runtime} +\section{JavaScript, TypeScript et \textit{runtime}} \begin{frame}{JavaScript} \centering @@ -267,7 +273,8 @@ \centering \includegraphics[width=.7\columnwidth]{img/jetbrains-ecosystem-2025-platforms.png} - \footnote[]{\url{https://devecosystem-2025.jetbrains.com/tools-and-trends}} + \addtocounter{footnote}{1} + \footnotetext{\url{https://devecosystem-2025.jetbrains.com/tools-and-trends}} \end{frame} \begin{frame}{TypeScript} @@ -313,6 +320,8 @@ \end{itemize} \end{frame} +\subsection{Typage statique et dynamique} + \begin{frame}{TypeScript : typage du code JavaScript} Types de base disponibles dans TypeScript~\footnote{\url{https://www.typescriptlang.org/docs/handbook/2/everyday-types.html}} : \begin{itemize} @@ -382,6 +391,26 @@ console.log(o.other); \end{minted} \end{frame} +\begin{frame}[fragile]{POJO et listes : \textit{Spread operator}} + \begin{minted}{ts} +const o1 = { "abc": 123 }; +// Ajout d'une nouvelle valeur dans `o1` +o1["def"] = 456; +// Nouvel objet avec copie des valeurs de `o1` +const o2 = { ...o1, "ghi": 789 }; + \end{minted} + + \vspace{.5cm} + + \begin{minted}{ts} +const l1 = [123]; +// Ajout d'une nouvelle valeur à la fin de `l1` +l1.append(456); +// Nouvelle liste avec copies des valeurs de `l1` +const l2 = [...l1, 789]; + \end{minted} +\end{frame} + \begin{frame}[fragile]{TypeScript : typer les objets avec \texttt{Record}} \begin{minted}{js} const o: Record = { @@ -583,12 +612,6 @@ const makeNoise = (animal: Animal) => { \end{minted} \end{frame} -\begin{frame}[fragile]{Généricité} - \begin{itemize} - \item \texttt{SomeContainer} - \end{itemize} -\end{frame} - \begin{frame}[fragile]{Exercice 3 : types au \textit{runtime}} \begin{minted}[fontsize=\footnotesize]{ts} interface Dog { @@ -612,22 +635,157 @@ makeNoise(dog); \end{minted} \end{frame} -\begin{frame}{Gestion des erreurs : exceptions} +\begin{frame}[fragile]{Type Guards : \textit{compile time} et \textit{run time}} \begin{itemize} - \item Exceptions - \item \texttt{try}/\texttt{catch} + \item \textit{Type narrowing} : \textit{compile time} + \item \textit{Type assertion} : \textit{run time} \end{itemize} + + \vspace{.5cm} + + \begin{minted}{ts} +interface Something { id: string }; + +export function isSomething(obj: unknown): obj is Something { + return !!obj + && typeof obj === "object" + && "id" in obj && typeof obj.id === "string"; +} + +const bla = { id: "blabla" }; + + \end{minted} \end{frame} -\begin{frame}{Programmation asynchrone} - TODO: Exemple (requête API) avec figure +\subsection{Généricité} +\begin{frame}[fragile]{Généricité} + \begin{itemize} + \item \texttt{SomeContainer} + \begin{itemize} + \item \texttt{T} est un type paramètre contraint à hériter ou implémenter \texttt{SomeData} + \item Permet de réutiliser le même conteneur pour différents types de données + \end{itemize} + \item Objectifs : + \begin{itemize} + \item Définir des structures et fonctions abstraites sur un type variable + \item Maintenir les contraintes de type + \end{itemize} + \end{itemize} + + \begin{columns} + \column{.6\textwidth} + \begin{minted}[fontsize=\footnotesize]{ts} +interface SomeData { + id: string; +} + +interface SpecializedData extends SomeData { + name: string; + value: number; +} + +interface SomeContainer { + data: T; +} + \end{minted} + + \column{.4\textwidth} + \begin{minted}[fontsize=\footnotesize]{ts} +const c: SomeContainer< + SpecializedData +> = { + data: { + id: "foo", + name: "Foo Bar", + value: 42, + }, +}; + \end{minted} + \end{columns} +\end{frame} + +\subsection{Gestion des erreurs} + +\begin{frame}[fragile]{Gestion des erreurs : exceptions} + \begin{minted}{ts} +const randInt = Math.floor(Math.random() * 3) + 1; // [1, 3] +const error = + randInt === 1 ? new TypeError() : + randInt === 2 ? new RangeError() : + new EvalError(); + +try { + throw error; +} catch { + // handle error +} + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Gestion des erreurs : exceptions} + \begin{minted}{ts} +const randInt = Math.floor(Math.random() * 3) + 1; // [1, 3] +const error = + randInt === 1 ? new TypeError() : + randInt === 2 ? new RangeError() : + new EvalError(); + +try { + throw error; +} catch (e) { + if (e instanceof TypeError) { + // handle TypeError + } else if (e instanceof RangeError) { + // handle RangeError + } else if (e instanceof EvalError) { + // handle EvalError + } +} + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Gestion des erreurs : exceptions} + \begin{minted}{ts} +try { + const file = await Deno.open("example.txt", { read: true }); + + for await (const chunk of Deno.iter(file)) { + console.log(new TextDecoder().decode(chunk)); + } +} catch (e) { + console.error("Error reading file:", e); +} finally { + // always close the file handle + if (file) { + file.close(); + } +} + \end{minted} +\end{frame} + +\subsection{Asynchronisme et parallélisme} + +\begin{frame}{Programmation asynchrone} Comment manipuler des données... \begin{itemize} \item que l'on n'a pas encore ? \item que l'on n'aura peut-être jamais ? \item qui l'on obtient dans un temps variable ? \end{itemize} + + Par exemple... + \begin{itemize} + \item le résultat d'une requête à une API ? + \item le contenu d'un fichier volumineux sur le disque ? + \item le résultat d'une requête à une base de données ? + \end{itemize} +\end{frame} + +\begin{frame}{Programmation asynchrone} + \centering + + \includegraphics[width=.9\textwidth]{img/async.png} \end{frame} \begin{frame}{Programmation asynchrone} @@ -644,7 +802,8 @@ makeNoise(dog); \centering \includegraphics[width=.8\textwidth]{img/promises.png} - \footnote[]{\url{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise}} + \addtocounter{footnote}{1} + \footnotetext{\url{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise}} \end{frame} \begin{frame}[fragile]{Programmation asynchrone} @@ -742,7 +901,23 @@ try { \begin{frame}{Programmation parallèle} \begin{itemize} \item \texttt{Promise.all} + \begin{itemize} + \item exécute toutes les Promises en \textbf{parallèle} + \item résout quand toutes les Promises ont \textbf{réussi} + \item rejette immédiatement si \textbf{une seule échoue} + \item retourne un tableau des résultats dans l'ordre initial des Promises + \end{itemize} + \item \texttt{Promise.allSettled} + \item \begin{itemize} + \item exécute toutes les Promises en \textbf{parallèle} + \item résout quand toutes les Promises sont terminées, \textbf{succès ou échec} + \item ne rejette \textbf{jamais} + \item retourne un tableau d'objets de type : + \begin{itemize} + \item \texttt{{status: 'fulfilled' | 'rejected', value | reason}} + \end{itemize} + \end{itemize} \end{itemize} \end{frame} @@ -871,6 +1046,8 @@ contents.forEach((result, i) => { \end{block} \end{frame} +\subsection{Environnement d'exécution} + \begin{frame}{Exécution : machine virtuelle} \begin{itemize} \item JavaScript : langage interprété dans une VM @@ -919,9 +1096,35 @@ contents.forEach((result, i) => { \begin{frame}{Gestion des dépendances} \begin{columns} \column{.8\textwidth} - Outils (npm, etc.) + Outils et registres : + \begin{itemize} + \item \texttt{npmjs.org} : registre historique des paquets JavaScript + \begin{itemize} + \item Gestionnaires de paquets : \textbf{npm / yarn} + \end{itemize} + \item \texttt{jsr.org} : "nouveau" registre, orienté TypeScript + \begin{itemize} + \item Gestionnaire de paquets : \textbf{jsr}, intégré dans Deno + \end{itemize} + \end{itemize} - Modules (ESM vs CommonJS) et imports + \vspace{.5cm} + + Modules et imports : + \begin{itemize} + \item \textbf{CommonJS (CJS)} : compatibilité historique + \begin{itemize} + \item Import : \texttt{const fs = require('fs');} + \item Export : \texttt{module.exports = \{ readFile \};} + \item Chargement synchrone + \end{itemize} + \item \textbf{ECMAScript Modules (ESM)} : standard moderne + \begin{itemize} + \item Import : \texttt{import \{ readFile \} from 'fs/promises';} + \item Export : \texttt{export const myFunc = () => \{...\};} + \item Chargement statique ou dynamique (asynchrone) + \end{itemize} + \end{itemize} \column{.2\textwidth} \includegraphics[width=\columnwidth]{img/npm.png} @@ -931,11 +1134,156 @@ contents.forEach((result, i) => { \section{Serveur web} -\begin{frame}{HTTP} +\begin{frame}{\textit{It's a series of tubes...}} \centering \includegraphics[width=.6\textwidth]{img/series-of-tubes.png} \end{frame} +\begin{frame}{Résumé de la méthode en TP} + \begin{enumerate} + \item La base de données retourne des \textbf{enregistrements} : ce sont des données arbitraires + \begin{itemize} + \item Élément unique : \texttt{Record | undefined} + \begin{itemize} + \item Peut être \texttt{undefined} si introuvable ! + \end{itemize} + \item Liste : \texttt{Record[]} + \begin{itemize} + \item Peut être vide ! + \end{itemize} + \end{itemize} + \item On doit \textit{vérifier le typage des données} pour que l'objet se conforme à son \textit{interface base de données} (\texttt{PollRow}, ...) + \item On peut alors \textit{convertir l'enregistrement} en objet de notre \textit{API} (\texttt{Poll}, ...) + \item L'objet peut être retourné au client de manière \textit{sûre} + \begin{itemize} + \item \textit{Compile time} : typage statique des données dans l'application client + \item \textit{Run time} : pas d'erreur à l'exécution pour l'utilisateur + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame} + \centering + \includegraphics[width=\textwidth]{img/type-guard-1.png} +\end{frame} + +\begin{frame}[noframenumbering] + \centering + \includegraphics[width=\textwidth]{img/type-guard-2.png} +\end{frame} + +\begin{frame}[noframenumbering] + \centering + \includegraphics[width=\textwidth]{img/type-guard-3.png} +\end{frame} + +\begin{frame}[noframenumbering] + \centering + \includegraphics[width=\textwidth]{img/type-guard-4.png} +\end{frame} + +\begin{frame}[noframenumbering] + \centering + \includegraphics[width=\textwidth]{img/type-guard-5.png} +\end{frame} + +\begin{frame}[fragile]{Résumé de la méthode en TP} + \begin{columns} + \column{.5\textwidth} + \begin{minted}{ts} +export interface PollRow { + id: string; + title: string; + description: string | null; + user_id: string | null; + created_at: string; + expires_at: string | null; + is_active: number; + [key: string]: SQLOutputValue; +} + \end{minted} + + \column{.5\textwidth} + \begin{minted}{ts} +export interface Poll { + id: string; + title: string; + description?: string; + options: PollOption[]; + userId?: string; + createdAt: string; + expiresAt?: string; + isActive: boolean; +} + \end{minted} + \end{columns} +\end{frame} + +\begin{frame}[fragile]{Résumé de la méthode en TP} + \begin{minted}[fontsize=\small]{ts} +export function isPollRow( + obj: Record +): obj is PollRow { + return !!obj && + typeof obj === "object" && + "id" in obj && typeof obj.id === "string" && + "title" in obj && typeof obj.title === "string" && + "description" in obj && + (typeof obj.description === "string" || obj.description === null) && + "user_id" in obj && + (typeof obj.user_id === "string" || obj.user_id === null) && + "created_at" in obj && typeof obj.created_at === "string" && + "expires_at" in obj && + (typeof obj.expires_at === "string" || obj.expires_at === null) && + "is_active" in obj && typeof obj.is_active === "number"; +} + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Résumé de la méthode en TP} + \begin{minted}{ts} +export function pollRowToApi( + row: PollRow, + optionRows: PollOptionRow[], +): Poll { + return { + id: row.id, + title: row.title, + description: row.description ?? undefined, + createdAt: row.created_at, + expiresAt: row.expires_at ?? undefined, + isActive: row.is_active === 1, + options: optionRows.map(pollOptionRowToApi), + }; +} + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Résumé de la méthode en TP} + \begin{minted}{ts} +export function pollApiToRow( + poll: Poll +): [PollRow, PollOptionRow[]] { + return [ + { + id: poll.id, + title: poll.title, + description: poll.description ?? null, + user_id: poll.userId ?? null, + created_at: poll.createdAt, + expires_at: poll.expiresAt ?? null, + is_active: Number(poll.isActive), + }, + poll.options.map((pollOption) => pollOptionApiToRow( + poll.id, pollOption + )), + ]; +} + \end{minted} +\end{frame} + +\subsection{Protocole HTTP} + \begin{frame}{HTTP} \begin{columns} \column{.6\textwidth} @@ -998,6 +1346,27 @@ contents.forEach((result, i) => { \end{itemize} \end{frame} +\begin{frame}{HTTP : architecture REST} + \begin{columns} + \column{.7\textwidth} + \begin{itemize} + \item \textit{REpresentational State Transfer}~\cite{fielding2000architectural} : défini par Roy Fielding en 2000, conçu pendant son doctorat (1996 - 1999) + \item Modèle d'architecture logicielle pour applications réseau + \item Manipuler des ressources distantes via des représentations textuelles + \item Opérations uniformes, prédéfinies, et \textbf{sans état} + \end{itemize} + + \column{.3\textwidth} + \includegraphics[width=\columnwidth]{img/roy_fielding_OSCON_2008.jpg} + \end{columns} +\end{frame} + +\begin{frame} + \begin{center} + \includegraphics[width=.8\textwidth]{img/rest-wikipedia.png} + \end{center} +\end{frame} + \begin{frame}[fragile]{Exercice 5 : un serveur HTTP minimal avec TypeScript} \begin{block}{Lire et écrire sur un socket TCP} Ci-dessous, la boucle principale pour ouvrir et écouter sur un socket TCP avec Deno. Dès la connexion d'un \textbf{client}, on passe le socket à la fonction \texttt{handleConn}. @@ -1038,14 +1407,14 @@ async function handleConn(conn: Deno.Conn) { \begin{frame}[fragile]{Exercice 5 : un serveur HTTP minimal avec TypeScript} \begin{minted}{sh} curl -X POST http://localhost:8080 \ - -H "Content-Type: text/plain" \ - --data "hello world" + -H "Content-Type: text/plain" \ + -d "hello world" \end{minted} \vspace{1cm} \begin{columns} - \column{.5\textwidth} + \column{.4\textwidth} \begin{minted}{text} POST / HTTP/1.1 Host: localhost:8080 @@ -1057,17 +1426,19 @@ Content-Length: 11 hello world \end{minted} - \column{.5\textwidth} + \column{.6\textwidth} \begin{alertblock}{Questions} \begin{enumerate} \item Quels sont les champs à inclure dans la réponse que l'on va retourner au client ? \item Compléter la fonction \texttt{handleConn} pour signifier une réponse OK comportant du texte au format JSON. - \item TODO: new Date().toUTCString(); + \item Comment va-t-on gérer l'horodatage ? \end{enumerate} \end{alertblock} \end{columns} \end{frame} +\subsection{Mise en œuvre} + \begin{frame}{Framework pour serveur HTTP : Oak} \begin{columns} \column{.8\textwidth} @@ -1079,6 +1450,7 @@ hello world \begin{itemize} \item Pour un point d'entrée donné, appliquer telle fonction, retourner telle valeur \item Requête : \texttt{GET http://blabla.com/item/123} + \item Méthode : \texttt{GET} \item Route : \texttt{/item/:itemId} \item Fonction : \texttt{getItem(id)} \item Réponse : \texttt{\{ id: 123, value: "..." \}} @@ -1090,22 +1462,233 @@ hello world \end{columns} \end{frame} -\begin{frame}{JSON} - +\begin{frame}[fragile]{Framework pour serveur HTTP : Oak} + \begin{minted}[fontsize=\footnotesize]{ts} +// Configuration +const BASE_URL = "http://localhost:8000"; +// Initialisation +const app = new Application(); +const router = new Router(); +// Middlewares +app.use(router.routes()); // Encore faut-il définir des routes ! ... +app.use(router.allowedMethods()); +// Événements +app.addEventListener( + "listen", () => console.log(`Server listening on ${BASE_URL}`), +); +app.addEventListener( + "error", (e) => console.log(`Uncaught error: ${e.message}`), +); +// Boucle principale +if (import.meta.main) { + await app.listen({ hostname: "localhost", port: 8000 }); +} +// Export +export { app }; + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Routage avec Oak : paramètres de la requête} + \begin{itemize} + \item L'utilisateur envoie une requête \texttt{GET} + \item Adresse : \texttt{http://localhost:8000/polls/abcd} + \end{itemize} + + \vspace{.5cm} + + \begin{minted}{ts} +router.get("/:pollId", (ctx) => { + const pollId = ctx.params.pollId; // abcd +}); + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Routage avec Oak : paramètres de la requête} + \begin{itemize} + \item L'utilisateur envoie une requête \texttt{POST} + \item Corps de la requête : un objet JSON + \end{itemize} + + \vspace{.5cm} + + \begin{minted}{json} +{ "foo": "bar" } + \end{minted} + + \begin{itemize} + \item Adresse : \texttt{http://localhost:8000/polls} + \end{itemize} + + \vspace{.5cm} + + \begin{minted}{ts} +router.post("/polls", async (ctx) => { + const createPollRequest = await ctx.request.body.json(); + // { "foo": "bar" } +}); + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Routage avec Oak : réponse à la requête} + \begin{minted}{ts} +export interface APISuccess { + success: true; + data: T; + error?: never; +} + +export interface APIFailure { + success: false; + data?: never; + error: APIError; +} + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Routage avec Oak : réponse à la requête} + \begin{minted}{ts} +export enum APIErrorCode { + NOT_FOUND = "NOT_FOUND", + SERVER_ERROR = "SERVER_ERROR", + TIMEOUT = "TIMEOUT", + UNAUTHORIZED = "UNAUTHORIZED", + VALIDATION_ERROR = "VALIDATION_ERROR", +} + +export interface APIError { + code: APIErrorCode; + message: string; +} + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Routage avec Oak : réponse à la requête} + \begin{itemize} + \item Et finalement, le type de \textbf{toute} réponse de l'API... + \end{itemize} + + \vspace{.5cm} + + \begin{minted}{ts} +export type APIResponse = APISuccess | APIFailure; + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Routage avec Oak : réponse à la requête} + \begin{itemize} + \item Si l'entité demandée est trouvée : + \end{itemize} + + \vspace{.5cm} + + \begin{minted}{ts} +const responseBody: APIResponse = { + success: true, + data: poll, +}; + +// Oak donne passe implicitement le code HTTP OK +ctx.response.body = responseBody; + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Routage avec Oak : réponse à la requête} + \begin{itemize} + \item Si l'entité demandée est introuvable : + \end{itemize} + + \vspace{.5cm} + + \begin{minted}{ts} +const responseBody: APIResponse = { + success: false, + error: { + code: APIErrorCode.NOT_FOUND, + message: "Poll not found", + }, +}; + +ctx.response.status = 404; +ctx.response.body = responseBody; + \end{minted} +\end{frame} + +\begin{frame}[fragile]{Routage avec Oak : réponse à la requête} + \begin{itemize} + \item Le serveur rencontre une erreur : + \end{itemize} + + \vspace{.5cm} + + \begin{minted}{ts} +const responseBody: APIResponse = { + success: false, + error: { + code: APIErrorCode.SERVER_ERROR, + message: "Failed to fetch poll", + }, +}; + +ctx.response.status = 500; +ctx.response.body = responseBody; + \end{minted} \end{frame} \begin{frame}{Middleware} - + \begin{itemize} + \item Code applicatif qui se positionne \textbf{entre la requête et la réponse} + \item \textbf{Intercepte} les données pour leur appliquer des modifications ou effectuer des effets de bord + \item En pratique : + \begin{itemize} + \item Une \textbf{fonction} qui prend en paramètre le \textbf{contexte} (\texttt{ctx}) et la \textbf{prochaine fonction} (\texttt{next}) + \item Invoquée dans une \textbf{chaîne} de traitement, ordonnée, pour chaque requête + \item Capable d'observer, modifier, court-circuiter ou ignorer le cycle de vie requête-réponse + \item Appelée par le framework (Oak) lors de la réception d'une requête + \end{itemize} + \end{itemize} \end{frame} -\begin{frame}{Middleware : CORS} - TODO: à mettre côté client ? +\begin{frame}[fragile]{Middleware} + \begin{minted}[fontsize=\small]{ts} +interface HelloWorldState { + hello?: string; + world?: string; +} +async function hello(ctx: Context, next: Next) { + ctx.state.hello = "Hello"; + await next(); +} +async function world(ctx: Context, next: Next) { + ctx.state.world = "world"; + await next(); +} +const router = new Router(); +router.get("/hello", hello, world, (ctx) => { + ctx.response.body = `${ctx.state.hello}, ${ctx.state.world}!`; +}); +const app = new Application(); + \end{minted} \end{frame} -\begin{frame}{Middleware : Gestion des erreurs} - +\begin{frame}[fragile]{Exercice 6 : gestion des erreurs du serveur} + \begin{block}{Un unique point de sortie pour les réponses d'erreur} + On veut éviter de dupliquer du code pour retourner des erreurs au client dans les fonctions de nos routes. On va donc utiliser un \textbf{middleware} qui sera chargé de remplir la réponse en cas d'erreur. + \end{block} + + \begin{minted}{ts} +export async function errorMiddleware(ctx: Context, next: Next) { } + \end{minted} + + \begin{alertblock}{Questions} + \begin{enumerate} + \item Sur quel mécanisme s'appuyer pour intercepter les erreurs levées dans les routes de l'application ? + \item Comment différencier une erreur \textit{prévue} d'une erreur \textit{inattendue} ? + \end{enumerate} + \end{alertblock} \end{frame} +\subsection{Gestion de l'état} + \begin{frame}{Base de données} \begin{columns} \column{.8\textwidth} @@ -1147,7 +1730,7 @@ CREATE TABLE thing_options ( \vspace{.5cm} \begin{minted}[fontsize=\footnotesize]{ts} -// Persist vote +// Enregistrer le vote const voteId = crypto.randomUUID(); const now = new Date().toISOString(); db.prepare( @@ -1156,7 +1739,7 @@ db.prepare( "VALUES (?, ?, ?, ?, ?);", ).run(voteId, pollId, optionId, userId || null, now); -// Increment tally +// Incrémenter le compteur db.prepare( "UPDATE poll_options SET vote_count = vote_count + 1 WHERE id = ?;", ).run(optionId); @@ -1173,18 +1756,47 @@ db.prepare( \begin{minted}{ts} try { - db.prepare("BEGIN").run(); // Start transaction + db.prepare("BEGIN").run(); // Début de transaction - db.prepare("...").run(); // Query 1 - db.prepare("...").run(); // Query 2 + db.prepare("...").run(); // Requête 1 + db.prepare("...").run(); // Requête 2 - db.prepare("COMMIT").run(); // Commit transaction + db.prepare("COMMIT").run(); // Transaction validée } catch { - db.prepare("ROLLBACK").run(); // Rollback on any error + db.prepare("ROLLBACK").run(); // Transaction annulée } \end{minted} \end{frame} +\begin{frame}{Exercice 7 : gestion des erreurs du serveur} + \begin{alertblock}{Besoin d'atomicité} + \begin{enumerate} + \item Quelles opérations doivent être \textbf{atomiques} dans notre application de sondage ? + \item Pourquoi ? C'est-à-dire : quels problèmes peuvent survenir si les sous-opérations, disjointes, échouent ? + \end{enumerate} + \end{alertblock} +\end{frame} + +\subsection{Architecture} + +\begin{frame}{Organisation du dépôt} + % TODO: pas de paradigme précis, responsabilité côté developpeur + + % TODO: reprendre la terminologie du framework (routes, middleware, etc.) + + \begin{center} + \includegraphics[width=.5\textwidth]{img/point.png} + \end{center} +\end{frame} + +\begin{frame}[fragile]{Exercice : ...} + TODO: proposer une organisation + + TODO: montrer les directives export / import associées +\end{frame} + +\subsection{Fiabilité et tests} + \begin{frame}[fragile]{Tests} \begin{minted}{ts} import { assertEquals, assert } from "@std/assert"; @@ -1205,6 +1817,10 @@ Deno.test({ \end{minted} \end{frame} +\begin{frame}[fragile]{Exercice ...} + TODO: écrire des tests pour une ou deux routes +\end{frame} + \section{Client web} \begin{frame}{HTTP, encore} @@ -1266,10 +1882,6 @@ Deno.test({ \section{Interactions} -\begin{frame}{REST} - -\end{frame} - \begin{frame}{Data Transfer Objects} \end{frame} @@ -1304,6 +1916,18 @@ Deno.test({ \end{itemize} \end{frame} +\begin{frame}{Nom d'hôte, port, adresse} + \begin{center} + \texttt{% + {\color{BrickRed}https}://% + {\color{MidnightBlue}foo.bar}.% + {\color{Orchid}com}% + [:{\color{LimeGreen}443}]/% + {\color{YellowOrange}baz.html}% + } + \end{center} +\end{frame} + \begin{frame}[fragile]{Reverse proxy} \begin{minted}[fontsize=\footnotesize]{text} http { @@ -1330,8 +1954,33 @@ http { \end{itemize} \end{frame} +\begin{frame}{CORS} + TODO: +\end{frame} + \section{Débuggage et profilage} +\begin{frame}{\textit{What could go wrong...}} + \begin{itemize} + \item Niveau client + \item Niveau serveur + \item Niveau base de données + \item Interactions API / BDD + \item Infrastructure + \item Sécurité + \item Déploiement + \item Gestion de l'état + \end{itemize} +\end{frame} + +\subsection{Outillage} + +\begin{frame}{\textit{Logging}} + TODO: +\end{frame} + +\subsection{Profilage des performances} + \begin{frame}{Côté serveur} \begin{columns} \column{.5\textwidth} @@ -1361,6 +2010,7 @@ http { \section{Bibliographie} \begin{frame}[allowframebreaks]{Références} + \nocite{*} \printbibliography[heading=none] \end{frame}