+
+
+
+
+
+
+
+
diff --git a/06-www/files/licenses/agpl-3.0.txt b/06-www/files/licenses/agpl-3.0.txt
new file mode 100644
index 0000000..be3f7b2
--- /dev/null
+++ b/06-www/files/licenses/agpl-3.0.txt
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
diff --git a/06-www/files/licenses/gpl-3.0.txt b/06-www/files/licenses/gpl-3.0.txt
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/06-www/files/licenses/gpl-3.0.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/06-www/files/selfie.jpg b/06-www/files/selfie.jpg
new file mode 100644
index 0000000..500071a
Binary files /dev/null and b/06-www/files/selfie.jpg differ
diff --git a/06-www/files/style.css b/06-www/files/style.css
new file mode 100644
index 0000000..7a64dac
--- /dev/null
+++ b/06-www/files/style.css
@@ -0,0 +1,536 @@
+/* MVP.css v1.14 - https://github.com/andybrewer/mvp */
+
+:root {
+ --active-brightness: 0.85;
+ --border-radius: 5px;
+ --box-shadow: 2px 2px 10px;
+ --color-accent: #118bee15;
+ --color-bg: #fff;
+ --color-bg-secondary: #e9e9e9;
+ --color-link: #118bee;
+ --color-secondary: #920de9;
+ --color-secondary-accent: #920de90b;
+ --color-shadow: #f4f4f4;
+ --color-table: #118bee;
+ --color-text: #000;
+ --color-text-secondary: #999;
+ --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+ --hover-brightness: 1.2;
+ --justify-important: center;
+ --justify-normal: left;
+ --line-height: 1.5;
+ --width-card: 285px;
+ --width-card-medium: 460px;
+ --width-card-wide: 800px;
+ --width-content: 1080px;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root[color-mode="user"] {
+ --color-accent: #0097fc4f;
+ --color-bg: #333;
+ --color-bg-secondary: #555;
+ --color-link: #0097fc;
+ --color-secondary: #e20de9;
+ --color-secondary-accent: #e20de94f;
+ --color-shadow: #bbbbbb20;
+ --color-table: #0097fc;
+ --color-text: #f7f7f7;
+ --color-text-secondary: #aaa;
+ }
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ html {
+ scroll-behavior: auto;
+ }
+}
+
+/* Layout */
+article aside {
+ background: var(--color-secondary-accent);
+ border-left: 4px solid var(--color-secondary);
+ padding: 0.01rem 0.8rem;
+}
+
+body {
+ background: var(--color-bg);
+ color: var(--color-text);
+ font-family: var(--font-family);
+ line-height: var(--line-height);
+ margin: 0;
+ overflow-x: hidden;
+ padding: 0;
+}
+
+footer,
+header,
+main {
+ margin: 0 auto;
+ max-width: var(--width-content);
+ padding: 3rem 1rem;
+}
+
+hr {
+ background-color: var(--color-bg-secondary);
+ border: none;
+ height: 1px;
+ margin: 4rem 0;
+ width: 100%;
+}
+
+section {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: var(--justify-important);
+}
+
+section img,
+article img {
+ max-width: 100%;
+}
+
+section pre {
+ overflow: auto;
+}
+
+section aside {
+ border: 1px solid var(--color-bg-secondary);
+ border-radius: var(--border-radius);
+ box-shadow: var(--box-shadow) var(--color-shadow);
+ margin: 1rem;
+ padding: 1.25rem;
+ width: var(--width-card);
+}
+
+section aside:hover {
+ box-shadow: var(--box-shadow) var(--color-bg-secondary);
+}
+
+[hidden] {
+ display: none;
+}
+
+/* Headers */
+article header,
+div header,
+main header {
+ padding-top: 0;
+}
+
+header {
+ text-align: var(--justify-important);
+}
+
+header a b,
+header a em,
+header a i,
+header a strong {
+ margin-left: 0.5rem;
+ margin-right: 0.5rem;
+}
+
+header nav img {
+ margin: 1rem 0;
+}
+
+section header {
+ padding-top: 0;
+ width: 100%;
+}
+
+/* Nav */
+nav {
+ align-items: center;
+ display: flex;
+ font-weight: bold;
+ justify-content: space-between;
+ margin-bottom: 7rem;
+}
+
+nav ul {
+ list-style: none;
+ padding: 0;
+}
+
+nav ul li {
+ display: inline-block;
+ margin: 0 0.5rem;
+ position: relative;
+ text-align: left;
+}
+
+/* Nav Dropdown */
+nav ul li:hover ul {
+ display: block;
+}
+
+nav ul li ul {
+ background: var(--color-bg);
+ border: 1px solid var(--color-bg-secondary);
+ border-radius: var(--border-radius);
+ box-shadow: var(--box-shadow) var(--color-shadow);
+ display: none;
+ height: auto;
+ left: -2px;
+ padding: .5rem 1rem;
+ position: absolute;
+ top: 1.7rem;
+ white-space: nowrap;
+ width: auto;
+ z-index: 1;
+}
+
+nav ul li ul::before {
+ /* fill gap above to make mousing over them easier */
+ content: "";
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: -0.5rem;
+ height: 0.5rem;
+}
+
+nav ul li ul li,
+nav ul li ul li a {
+ display: block;
+}
+
+/* Typography */
+code,
+samp {
+ background-color: var(--color-accent);
+ border-radius: var(--border-radius);
+ color: var(--color-text);
+ display: inline-block;
+ margin: 0 0.1rem;
+ padding: 0 0.5rem;
+}
+
+details {
+ margin: 1.3rem 0;
+}
+
+details summary {
+ font-weight: bold;
+ cursor: pointer;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ line-height: var(--line-height);
+ text-wrap: balance;
+}
+
+mark {
+ padding: 0.1rem;
+}
+
+ol li,
+ul li {
+ padding: 0.2rem 0;
+}
+
+p {
+ margin: 0.75rem 0;
+ padding: 0;
+ width: 100%;
+}
+
+pre {
+ margin: 1rem 0;
+ max-width: var(--width-card-wide);
+ padding: 1rem 0;
+}
+
+pre code,
+pre samp {
+ display: block;
+ max-width: var(--width-card-wide);
+ padding: 0.5rem 2rem;
+ white-space: pre-wrap;
+}
+
+small {
+ color: var(--color-text-secondary);
+}
+
+sup {
+ background-color: var(--color-secondary);
+ border-radius: var(--border-radius);
+ color: var(--color-bg);
+ font-size: xx-small;
+ font-weight: bold;
+ margin: 0.2rem;
+ padding: 0.2rem 0.3rem;
+ position: relative;
+ top: -2px;
+}
+
+/* Links */
+a {
+ color: var(--color-link);
+ display: inline-block;
+ font-weight: bold;
+ text-decoration: underline;
+}
+
+a:active {
+ filter: brightness(var(--active-brightness));
+}
+
+a:hover {
+ filter: brightness(var(--hover-brightness));
+}
+
+a b,
+a em,
+a i,
+a strong,
+button,
+input[type="submit"] {
+ border-radius: var(--border-radius);
+ display: inline-block;
+ font-size: medium;
+ font-weight: bold;
+ line-height: var(--line-height);
+ margin: 0.5rem 0;
+ padding: 1rem 2rem;
+}
+
+button,
+input[type="submit"] {
+ font-family: var(--font-family);
+}
+
+button:active,
+input[type="submit"]:active {
+ filter: brightness(var(--active-brightness));
+}
+
+button:hover,
+input[type="submit"]:hover {
+ cursor: pointer;
+ filter: brightness(var(--hover-brightness));
+}
+
+a b,
+a strong,
+button,
+input[type="submit"] {
+ background-color: var(--color-link);
+ border: 2px solid var(--color-link);
+ color: var(--color-bg);
+}
+
+a em,
+a i {
+ border: 2px solid var(--color-link);
+ border-radius: var(--border-radius);
+ color: var(--color-link);
+ display: inline-block;
+ padding: 1rem 2rem;
+}
+
+article aside a {
+ color: var(--color-secondary);
+}
+
+/* Images */
+figure {
+ margin: 0;
+ padding: 0;
+}
+
+figure img {
+ max-width: 100%;
+}
+
+figure figcaption {
+ color: var(--color-text-secondary);
+}
+
+/* Forms */
+button:disabled,
+input:disabled {
+ background: var(--color-bg-secondary);
+ border-color: var(--color-bg-secondary);
+ color: var(--color-text-secondary);
+ cursor: not-allowed;
+}
+
+button[disabled]:hover,
+input[type="submit"][disabled]:hover {
+ filter: none;
+}
+
+form {
+ border: 1px solid var(--color-bg-secondary);
+ border-radius: var(--border-radius);
+ box-shadow: var(--box-shadow) var(--color-shadow);
+ display: block;
+ max-width: var(--width-card-wide);
+ min-width: var(--width-card);
+ padding: 1.5rem;
+ text-align: var(--justify-normal);
+}
+
+form header {
+ margin: 1.5rem 0;
+ padding: 1.5rem 0;
+}
+
+input,
+label,
+select,
+textarea {
+ display: block;
+ font-size: inherit;
+ max-width: var(--width-card-wide);
+}
+
+input[type="checkbox"],
+input[type="radio"] {
+ display: inline-block;
+}
+
+input[type="checkbox"]+label,
+input[type="radio"]+label {
+ display: inline-block;
+ font-weight: normal;
+ position: relative;
+ top: 1px;
+}
+
+input[type="range"] {
+ padding: 0.4rem 0;
+}
+
+input,
+select,
+textarea {
+ border: 1px solid var(--color-bg-secondary);
+ border-radius: var(--border-radius);
+ margin-bottom: 1rem;
+ padding: 0.4rem 0.8rem;
+}
+
+input[type="text"],
+textarea {
+ width: calc(100% - 1.6rem);
+}
+
+input[readonly],
+textarea[readonly] {
+ background-color: var(--color-bg-secondary);
+}
+
+label {
+ font-weight: bold;
+ margin-bottom: 0.2rem;
+}
+
+/* Popups */
+dialog {
+ border: 1px solid var(--color-bg-secondary);
+ border-radius: var(--border-radius);
+ box-shadow: var(--box-shadow) var(--color-shadow);
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 50%;
+ z-index: 999;
+}
+
+/* Tables */
+table {
+ border: 1px solid var(--color-bg-secondary);
+ border-radius: var(--border-radius);
+ border-spacing: 0;
+ display: inline-block;
+ max-width: 100%;
+ overflow-x: auto;
+ padding: 0;
+ white-space: nowrap;
+}
+
+table td,
+table th,
+table tr {
+ padding: 0.4rem 0.8rem;
+ text-align: var(--justify-important);
+}
+
+table thead {
+ background-color: var(--color-table);
+ border-collapse: collapse;
+ border-radius: var(--border-radius);
+ color: var(--color-bg);
+ margin: 0;
+ padding: 0;
+}
+
+table thead th:first-child {
+ border-top-left-radius: var(--border-radius);
+}
+
+table thead th:last-child {
+ border-top-right-radius: var(--border-radius);
+}
+
+table thead th:first-child,
+table tr td:first-child {
+ text-align: var(--justify-normal);
+}
+
+table tr:nth-child(even) {
+ background-color: var(--color-accent);
+}
+
+/* Quotes */
+blockquote {
+ display: block;
+ font-size: x-large;
+ line-height: var(--line-height);
+ margin: 1rem auto;
+ max-width: var(--width-card-medium);
+ padding: 1.5rem 1rem;
+ text-align: var(--justify-important);
+}
+
+blockquote footer {
+ color: var(--color-text-secondary);
+ display: block;
+ font-size: small;
+ line-height: var(--line-height);
+ padding: 1.5rem 0;
+}
+
+/* Scrollbars */
+* {
+ scrollbar-width: thin;
+ scrollbar-color: rgb(202, 202, 232) auto;
+}
+
+*::-webkit-scrollbar {
+ width: 5px;
+ height: 5px;
+}
+
+*::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+*::-webkit-scrollbar-thumb {
+ background-color: rgb(202, 202, 232);
+ border-radius: 10px;
+}
diff --git a/06-www/logger-unsafe.mjs b/06-www/logger-unsafe.mjs
new file mode 100644
index 0000000..caad244
--- /dev/null
+++ b/06-www/logger-unsafe.mjs
@@ -0,0 +1,9 @@
+import fs from "fs/promises";
+
+const logFh = await fs.open("./access.log", "a");
+
+async function log(msg) {
+ await logFh.write(`${msg}\n`);
+}
+
+export { log };
diff --git a/06-www/logger.mjs b/06-www/logger.mjs
new file mode 100644
index 0000000..17c5e71
--- /dev/null
+++ b/06-www/logger.mjs
@@ -0,0 +1,11 @@
+import fs from "fs/promises";
+
+const logFh = await fs.open("./access.log", "a");
+const logStream = logFh.createWriteStream();
+
+const log = function (msg) {
+ console.log(msg);
+ logStream.write(`${msg}\n`);
+};
+
+export { log };
diff --git a/06-www/media-types.mjs b/06-www/media-types.mjs
new file mode 100644
index 0000000..57764e7
--- /dev/null
+++ b/06-www/media-types.mjs
@@ -0,0 +1,26 @@
+const types = {
+ "text/html": ["html", "htm"],
+ "text/plain": ["txt", "text"],
+ "text/css": ["css"],
+ "application/javascript": ["js", "mjs"],
+ "application/json": ["json"],
+ "image/jpeg": ["jpeg", "jpg"],
+ "image/x-icon": ["ico"],
+ "image/avif": ["avif"],
+ "audio/mpeg": ["mpga", "mp2", "mp2a", "mp3", "m2a", "m3a"],
+ "video/mp4": ["mp4", "mp4v", "mpg4"],
+ "video/mpeg": ["mpeg", "mpg", "mpe", "m1v", "m2v"],
+ "video/ogg": ["ogv"],
+ "video/quicktime": ["qt", "mov"],
+ "video/x-matroska": ["mkv", "mk3d", "mks"],
+ "video/x-msvideo": ["avi"],
+};
+const extMap = new Map();
+
+for (let [type, extensions] of Object.entries(types)) {
+ for (let extension of extensions) {
+ extMap.set(`.${extension}`, type);
+ }
+}
+
+export default extMap;
diff --git a/06-www/w3-utils.mjs b/06-www/w3-utils.mjs
new file mode 100644
index 0000000..70016de
--- /dev/null
+++ b/06-www/w3-utils.mjs
@@ -0,0 +1,64 @@
+import fs from "fs/promises";
+import { hrtime } from "process";
+
+async function exitIfNotDir(path) {
+ try {
+ const pathStat = await fs.stat(path);
+ if (!pathStat.isDirectory()) {
+ console.error(`${path} is not a directory.`);
+ process.exit(1);
+ }
+ } catch {
+ console.error(`${path} does not exists or cannot be opened.`);
+ process.exit(1);
+ }
+}
+
+function isStringTrue(str) {
+ if (str === "true") {
+ return true;
+ }
+ return false;
+}
+
+function hasTrailingSlash(path) {
+ return path[path.length - 1] === "/";
+}
+
+async function tryOpenFile(path, index) {
+ let file = path;
+ if (hasTrailingSlash(path)) {
+ if (index) {
+ file = `${path}index.html`;
+ } else {
+ return { found: false };
+ }
+ }
+
+ let fh;
+ try {
+ fh = await fs.open(file);
+ } catch (e) {
+ console.error(e);
+ return { found: false };
+ }
+
+ const fileStat = await fh.stat();
+ if (fileStat.isDirectory()) {
+ //spiegare caso eccezionale - così agganciarsi ai tanti controlli di sicurezza da fare nel caso reale
+ await fh.close();
+ return { found: false };
+ }
+ return { found: true, fh, fileStat };
+}
+
+function generateLogString(req, res, pathname, startTime) {
+ const endTime = hrtime.bigint();
+ const duration = (endTime - startTime) / BigInt(1e6);
+ const timestamp = new Date().toISOString();
+ const ip = req.socket.remoteAddress;
+ const httpDetails = `"${req.method} ${pathname}" ${res.statusCode}`;
+ return `${ip} - ${timestamp} ${httpDetails} - ${duration}ms`;
+}
+
+export { exitIfNotDir, isStringTrue, tryOpenFile, generateLogString };
diff --git a/07-eloop/01-linear-prime.mjs b/07-eloop/01-linear-prime.mjs
new file mode 100644
index 0000000..c1da203
--- /dev/null
+++ b/07-eloop/01-linear-prime.mjs
@@ -0,0 +1,5 @@
+import { isPrime } from "./cpu-intensive.mjs";
+
+console.log("Script started");
+console.log(`Is it prime? ${isPrime(2971215073)}`);
+console.log("Script ended");
diff --git a/07-eloop/02-callback-prime.mjs b/07-eloop/02-callback-prime.mjs
new file mode 100644
index 0000000..e98541a
--- /dev/null
+++ b/07-eloop/02-callback-prime.mjs
@@ -0,0 +1,13 @@
+import { PRIME_BIG, isPrime } from "./cpu-intensive.mjs";
+
+function calcPrime(num, cb) {
+ const isNumPrime = isPrime(num);
+ cb(isNumPrime);
+}
+console.log("Script started");
+
+calcPrime(PRIME_BIG, (res) => {
+ console.log(`${PRIME_BIG} is prime? ${res}`);
+});
+
+console.log("Script ended");
diff --git a/07-eloop/03-callback-timeout.mjs b/07-eloop/03-callback-timeout.mjs
new file mode 100644
index 0000000..31e99ac
--- /dev/null
+++ b/07-eloop/03-callback-timeout.mjs
@@ -0,0 +1,15 @@
+import { PRIME_BIG, isPrime } from "./cpu-intensive.mjs";
+
+function calcPrime(num, cb) {
+ setTimeout(() => {
+ const isNumPrime = isPrime(num);
+ cb(isNumPrime);
+ }, 1000);
+}
+console.log("Script started");
+
+calcPrime(PRIME_BIG, (res) => {
+ console.log(`${PRIME_BIG} is prime? ${res}`);
+});
+
+console.log("Script ended (?)");
diff --git a/07-eloop/04-timers-poll-check.mjs b/07-eloop/04-timers-poll-check.mjs
new file mode 100644
index 0000000..06ed493
--- /dev/null
+++ b/07-eloop/04-timers-poll-check.mjs
@@ -0,0 +1,18 @@
+import fs from "fs";
+import { isPrime } from "./cpu-intensive.mjs";
+
+console.log("running the script...");
+
+fs.readFile("./number.txt", { encoding: "utf-8" }, (err, data) => {
+ const numToCheck = Number.parseInt(data);
+ console.log(`Number to check: ${numToCheck}`);
+
+ const primeRes = isPrime(numToCheck);
+
+ setTimeout(() => {
+ console.log(`[timeout] ${numToCheck} is prime? ${primeRes}`);
+ }, 0);
+ setImmediate(() => {
+ console.log(`[immediate] ${numToCheck} is prime? ${primeRes}`);
+ });
+});
diff --git a/07-eloop/05-draining-queues.mjs b/07-eloop/05-draining-queues.mjs
new file mode 100644
index 0000000..82e07ea
--- /dev/null
+++ b/07-eloop/05-draining-queues.mjs
@@ -0,0 +1,18 @@
+setTimeout(() => {
+ console.log(`[timeout] 1`);
+}, 0);
+setImmediate(() => {
+ console.log(`[immediate] 1`);
+});
+setTimeout(() => {
+ console.log(`[timeout] 2`);
+}, 0);
+setImmediate(() => {
+ console.log(`[immediate] 2`);
+});
+setTimeout(() => {
+ console.log(`[timeout] 3`);
+}, 0);
+setImmediate(() => {
+ console.log(`[immediate] 3`);
+});
diff --git a/07-eloop/06-scheduling-timers.mjs b/07-eloop/06-scheduling-timers.mjs
new file mode 100644
index 0000000..12017e8
--- /dev/null
+++ b/07-eloop/06-scheduling-timers.mjs
@@ -0,0 +1,16 @@
+setTimeout(() => {
+ console.log(`[timeout 0]`);
+}, 0);
+setImmediate(() => {
+ console.log(`[immediate 0]`);
+ setTimeout(() => {
+ console.log(`[timeout] scheduled from immediate 0`);
+ }, 0);
+ setImmediate(() => {
+ console.log(`[immediate] scheduled from immediate 0`);
+ });
+ let i = 0;
+ while (i < 10000000) {
+ i++;
+ }
+});
diff --git a/07-eloop/07-microtasks.mjs b/07-eloop/07-microtasks.mjs
new file mode 100644
index 0000000..fee182f
--- /dev/null
+++ b/07-eloop/07-microtasks.mjs
@@ -0,0 +1,17 @@
+setImmediate(() => {
+ console.log("first immediate");
+ Promise.resolve()
+ .then(() => {
+ console.log("then 1");
+ return;
+ })
+ .then(() => {
+ console.log("then 2");
+ });
+ queueMicrotask(() => {
+ console.log("queueMicrotask");
+ });
+});
+setImmediate(() => {
+ console.log("last immediate");
+});
diff --git a/07-eloop/08-next-tick.mjs b/07-eloop/08-next-tick.mjs
new file mode 100644
index 0000000..ed9a041
--- /dev/null
+++ b/07-eloop/08-next-tick.mjs
@@ -0,0 +1,20 @@
+setImmediate(() => {
+ console.log("first immediate");
+ Promise.resolve()
+ .then(() => {
+ console.log("then 1");
+ return;
+ })
+ .then(() => {
+ console.log("then 2");
+ });
+ queueMicrotask(() => {
+ console.log("queueMicrotask");
+ });
+ process.nextTick(() => {
+ console.log("next tick");
+ });
+});
+setImmediate(() => {
+ console.log("last immediate");
+});
diff --git a/07-eloop/09-partitioned.mjs b/07-eloop/09-partitioned.mjs
new file mode 100644
index 0000000..2c0a20e
--- /dev/null
+++ b/07-eloop/09-partitioned.mjs
@@ -0,0 +1,14 @@
+import { PRIME_MED, isPrimeAsync } from "./cpu-intensive.mjs";
+import { hrtime } from "process";
+
+let lastTime = hrtime.bigint();
+const interval = setInterval(() => {
+ const now = hrtime.bigint();
+ const duration = (now - lastTime) / BigInt(1e6);
+ console.log(`[interval] ${duration}ms passed since last one`);
+}, 500);
+
+isPrimeAsync(PRIME_MED, function (res) {
+ console.log(`${PRIME_MED} is prime? ${res}`);
+ clearInterval(interval);
+});
diff --git a/07-eloop/10-http-prime-blocking.mjs b/07-eloop/10-http-prime-blocking.mjs
new file mode 100644
index 0000000..a36e056
--- /dev/null
+++ b/07-eloop/10-http-prime-blocking.mjs
@@ -0,0 +1,26 @@
+import http from "http";
+import { isPrime } from "./cpu-intensive.mjs";
+import { generateLogString } from "./eloop-utils.mjs";
+import { hrtime } from "process";
+
+const server = http.createServer();
+server.on("request", async (req, res) => {
+ const startTime = hrtime.bigint();
+
+ const { searchParams, pathname } = new URL(
+ req.url,
+ `http://${req.headers.host}`
+ );
+ const num = parseInt(searchParams.get("num"));
+ res.on("finish", () => {
+ console.log(generateLogString(req, res, pathname, startTime));
+ });
+
+ const primeRes = isPrime(num);
+
+ res.end(`${num} is prime? ${primeRes}`);
+});
+
+server.listen(3000, "127.0.0.1", () => {
+ console.log(`Web server running`);
+});
diff --git a/07-eloop/11-http-prime-partitioned.mjs b/07-eloop/11-http-prime-partitioned.mjs
new file mode 100644
index 0000000..1ef1bff
--- /dev/null
+++ b/07-eloop/11-http-prime-partitioned.mjs
@@ -0,0 +1,26 @@
+import http from "http";
+import { isPrimeAsync } from "./cpu-intensive.mjs";
+import { generateLogString } from "./eloop-utils.mjs";
+import { hrtime } from "process";
+
+const server = http.createServer();
+server.on("request", async (req, res) => {
+ const startTime = hrtime.bigint();
+
+ const { searchParams, pathname } = new URL(
+ req.url,
+ `http://${req.headers.host}`
+ );
+ const num = parseInt(searchParams.get("num"));
+ res.on("finish", () => {
+ console.log(generateLogString(req, res, pathname, startTime));
+ });
+
+ isPrimeAsync(num, (primeRes) => {
+ res.end(`${num} is prime? ${primeRes}`);
+ });
+});
+
+server.listen(3000, "127.0.0.1", () => {
+ console.log(`Web server running`);
+});
diff --git a/07-eloop/12-child-exec-file.mjs b/07-eloop/12-child-exec-file.mjs
new file mode 100644
index 0000000..ba36246
--- /dev/null
+++ b/07-eloop/12-child-exec-file.mjs
@@ -0,0 +1,11 @@
+import { execFile } from "child_process";
+
+execFile("node", ["isprime.mjs", 3], (error, stdout, stderr) => {
+ if (error) {
+ console.log("The number is NOT prime");
+ } else {
+ console.log("The number is prime");
+ }
+ console.log(`stdout: ${stdout}`);
+ console.log(`stderr: ${stderr}`);
+});
diff --git a/07-eloop/13-child-exec.mjs b/07-eloop/13-child-exec.mjs
new file mode 100644
index 0000000..96af298
--- /dev/null
+++ b/07-eloop/13-child-exec.mjs
@@ -0,0 +1,9 @@
+import { exec } from "child_process";
+
+exec("./isprime.mjs 3", (error, stdout, stderr) => {
+ if (error) {
+ console.log("The number is NOT prime");
+ } else {
+ console.log("The number is prime");
+ }
+});
diff --git a/07-eloop/14-http-prime-child-node.mjs b/07-eloop/14-http-prime-child-node.mjs
new file mode 100644
index 0000000..ea3ebec
--- /dev/null
+++ b/07-eloop/14-http-prime-child-node.mjs
@@ -0,0 +1,30 @@
+import http from "http";
+import { generateLogString } from "./eloop-utils.mjs";
+import { hrtime } from "process";
+import { exec } from "child_process";
+
+const server = http.createServer();
+server.on("request", async (req, res) => {
+ const startTime = hrtime.bigint();
+
+ const { searchParams, pathname } = new URL(
+ req.url,
+ `http://${req.headers.host}`
+ );
+ const num = parseInt(searchParams.get("num"));
+ res.on("finish", () => {
+ console.log(generateLogString(req, res, pathname, startTime));
+ });
+
+ exec(`node isprime.mjs ${num}`, (error, stdout, stderr) => {
+ if (error) {
+ res.end(`${num} is prime? false`);
+ } else {
+ res.end(`${num} is prime? true`);
+ }
+ });
+});
+
+server.listen(3000, "127.0.0.1", () => {
+ console.log(`Web server running`);
+});
diff --git a/07-eloop/15-http-prime-child-c.mjs b/07-eloop/15-http-prime-child-c.mjs
new file mode 100644
index 0000000..72b12f8
--- /dev/null
+++ b/07-eloop/15-http-prime-child-c.mjs
@@ -0,0 +1,30 @@
+import http from "http";
+import { generateLogString } from "./eloop-utils.mjs";
+import { hrtime } from "process";
+import { exec } from "child_process";
+
+const server = http.createServer();
+server.on("request", async (req, res) => {
+ const startTime = hrtime.bigint();
+
+ const { searchParams, pathname } = new URL(
+ req.url,
+ `http://${req.headers.host}`
+ );
+ const num = parseInt(searchParams.get("num"));
+ res.on("finish", () => {
+ console.log(generateLogString(req, res, pathname, startTime));
+ });
+
+ exec(`./isprime ${num}`, (error, stdout, stderr) => {
+ if (error) {
+ res.end(`${num} is prime? false`);
+ } else {
+ res.end(`${num} is prime? true`);
+ }
+ });
+});
+
+server.listen(3000, "127.0.0.1", () => {
+ console.log(`Web server running`);
+});
diff --git a/07-eloop/16-child-spawn.mjs b/07-eloop/16-child-spawn.mjs
new file mode 100644
index 0000000..a6df6fc
--- /dev/null
+++ b/07-eloop/16-child-spawn.mjs
@@ -0,0 +1,15 @@
+import { spawn } from "child_process";
+
+const ls = spawn("ls");
+
+ls.stdout.on("data", (data) => {
+ console.log(`ls stdout ${data}`);
+});
+ls.stderr.on("data", (data) => {
+ console.error(`ls stderr: ${data}`);
+});
+ls.on("close", (code) => {
+ if (code !== 0) {
+ console.log(`ls process exited with code ${code}`);
+ }
+});
diff --git a/07-eloop/17-child-spawn-pipe.mjs b/07-eloop/17-child-spawn-pipe.mjs
new file mode 100644
index 0000000..557fb16
--- /dev/null
+++ b/07-eloop/17-child-spawn-pipe.mjs
@@ -0,0 +1,33 @@
+import { spawn } from "child_process";
+
+const ls = spawn("ls", ["*.mjs"], { shell: true });
+const grep = spawn("grep", ["http"]);
+
+ls.stdout.on("data", (data) => {
+ grep.stdin.write(data);
+});
+
+ls.stderr.on("data", (data) => {
+ console.error(`ls stderr: ${data}`);
+});
+
+ls.on("close", (code) => {
+ if (code !== 0) {
+ console.log(`ls process exited with code ${code}`);
+ }
+ grep.stdin.end();
+});
+
+grep.stdout.on("data", (data) => {
+ console.log(data.toString());
+});
+
+grep.stderr.on("data", (data) => {
+ console.error(`grep stderr: ${data}`);
+});
+
+grep.on("close", (code) => {
+ if (code !== 0) {
+ console.log(`grep process exited with code ${code}`);
+ }
+});
diff --git a/07-eloop/18-http-prime-child-spawn.mjs b/07-eloop/18-http-prime-child-spawn.mjs
new file mode 100644
index 0000000..d1234bf
--- /dev/null
+++ b/07-eloop/18-http-prime-child-spawn.mjs
@@ -0,0 +1,31 @@
+import http from "http";
+import { generateLogString } from "./eloop-utils.mjs";
+import { hrtime } from "process";
+import { spawn } from "child_process";
+
+const server = http.createServer();
+server.on("request", async (req, res) => {
+ const startTime = hrtime.bigint();
+
+ const { searchParams, pathname } = new URL(
+ req.url,
+ `http://${req.headers.host}`
+ );
+ const num = parseInt(searchParams.get("num"));
+ res.on("finish", () => {
+ console.log(generateLogString(req, res, pathname, startTime));
+ });
+
+ const proc = spawn("node", ["isprime.mjs", num]);
+ proc.on("close", (code) => {
+ if (code !== 0) {
+ res.end(`${num} is prime? false`);
+ } else {
+ res.end(`${num} is prime? true`);
+ }
+ });
+});
+
+server.listen(3000, "127.0.0.1", () => {
+ console.log(`Web server running`);
+});
diff --git a/07-eloop/19-child-fork.mjs b/07-eloop/19-child-fork.mjs
new file mode 100644
index 0000000..0a35a70
--- /dev/null
+++ b/07-eloop/19-child-fork.mjs
@@ -0,0 +1,15 @@
+import { fork } from "child_process";
+import { PRIME_HUGE } from "./cpu-intensive.mjs";
+
+const interval = setInterval(() => {
+ console.log(
+ `${new Date().toISOString()} - isprime() is still running in another process..`
+ );
+}, 1000);
+
+const child = fork("isprime-child.mjs");
+child.send(PRIME_HUGE);
+child.on("message", (result) => {
+ console.log(`${PRIME_HUGE} is prime? ${result}`);
+ clearInterval(interval);
+});
diff --git a/07-eloop/20-http-prime-child-fork.mjs b/07-eloop/20-http-prime-child-fork.mjs
new file mode 100644
index 0000000..7439e01
--- /dev/null
+++ b/07-eloop/20-http-prime-child-fork.mjs
@@ -0,0 +1,32 @@
+import http from "http";
+import { generateLogString } from "./eloop-utils.mjs";
+import { hrtime } from "process";
+import { fork } from "child_process";
+
+const server = http.createServer();
+server.on("request", async (req, res) => {
+ const startTime = hrtime.bigint();
+
+ const { searchParams, pathname } = new URL(
+ req.url,
+ `http://${req.headers.host}`
+ );
+ const num = parseInt(searchParams.get("num"));
+ res.on("finish", () => {
+ console.log(generateLogString(req, res, pathname, startTime));
+ });
+
+ const child = fork("isprime-child.mjs");
+ child.send(num);
+ child.on("message", (result) => {
+ if (result) {
+ res.end(`${num} is prime? true`);
+ } else {
+ res.end(`${num} is prime? false`);
+ }
+ });
+});
+
+server.listen(3000, "127.0.0.1", () => {
+ console.log(`Web server running`);
+});
diff --git a/07-eloop/21-self-fork.mjs b/07-eloop/21-self-fork.mjs
new file mode 100644
index 0000000..0ad5379
--- /dev/null
+++ b/07-eloop/21-self-fork.mjs
@@ -0,0 +1,22 @@
+import { PRIME_HUGE, isPrime } from "./cpu-intensive.mjs";
+import { fork } from "child_process";
+
+if (process.argv[2] === "child") {
+ process.on("message", (msg) => {
+ const result = isPrime(msg);
+ process.send(result);
+ process.exit();
+ });
+} else {
+ const interval = setInterval(() => {
+ console.log(
+ `${new Date().toISOString()} - isprime() is still running in another process..`
+ );
+ }, 1000);
+ const child = fork("21-self-fork.mjs", ["child"]);
+ child.send(PRIME_HUGE);
+ child.on("message", (result) => {
+ console.log(`${PRIME_HUGE} is prime? ${result}`);
+ clearInterval(interval);
+ });
+}
diff --git a/07-eloop/22-http-prime-cluster.mjs b/07-eloop/22-http-prime-cluster.mjs
new file mode 100644
index 0000000..f3f6290
--- /dev/null
+++ b/07-eloop/22-http-prime-cluster.mjs
@@ -0,0 +1,41 @@
+import cluster from "cluster";
+import http from "http";
+import { availableParallelism } from "os";
+import process, { hrtime } from "process";
+import { generateLogString } from "./eloop-utils.mjs";
+import { isPrime } from "./cpu-intensive.mjs";
+
+const numCPUs = availableParallelism();
+
+if (cluster.isPrimary) {
+ console.log(`Primary ${process.pid} is running`);
+
+ for (let i = 0; i < numCPUs; i++) {
+ cluster.fork();
+ }
+
+ cluster.on("exit", (worker, code) => {
+ console.log(`worker ${worker.process.pid} exited with code ${code}`);
+ });
+} else {
+ const server = http.createServer();
+ server.on("request", (req, res) => {
+ const startTime = hrtime.bigint();
+
+ const { searchParams, pathname } = new URL(
+ req.url,
+ `http://${req.headers.host}`
+ );
+ const num = parseInt(searchParams.get("num"));
+ res.on("finish", () => {
+ const logMsg = generateLogString(req, res, pathname, startTime);
+ console.log(`${process.pid} - ${logMsg}`);
+ });
+
+ const primeRes = isPrime(num);
+ res.end(`${num} is prime? ${primeRes}`);
+ });
+ server.listen(3000);
+
+ console.log(`Worker ${process.pid} started`);
+}
diff --git a/07-eloop/23-worker-thread.mjs b/07-eloop/23-worker-thread.mjs
new file mode 100644
index 0000000..be0a4c8
--- /dev/null
+++ b/07-eloop/23-worker-thread.mjs
@@ -0,0 +1,22 @@
+import { isMainThread, workerData, Worker, parentPort } from "worker_threads";
+import { PRIME_HUGE, isPrime } from "./cpu-intensive.mjs";
+
+if (isMainThread) {
+ console.log(`Main thread pid is ${process.pid}`);
+ const interval = setInterval(() => {
+ console.log(
+ `${new Date().toISOString()} - isprime() is still running in a worker thread..`
+ );
+ }, 1000);
+ const worker = new Worker("./23-worker-thread.mjs", {
+ workerData: { num: PRIME_HUGE },
+ });
+ worker.on("message", (result) => {
+ console.log(`${PRIME_HUGE} is prime? ${result}`);
+ clearInterval(interval);
+ });
+} else {
+ const result = isPrime(workerData.num);
+ parentPort.postMessage(result);
+ process.exit();
+}
diff --git a/07-eloop/24-http-prime-workers.mjs b/07-eloop/24-http-prime-workers.mjs
new file mode 100644
index 0000000..3b3a56d
--- /dev/null
+++ b/07-eloop/24-http-prime-workers.mjs
@@ -0,0 +1,35 @@
+import http from "http";
+import process, { hrtime } from "process";
+import { generateLogString } from "./eloop-utils.mjs";
+import { isPrime } from "./cpu-intensive.mjs";
+import { Worker, isMainThread, parentPort, workerData } from "worker_threads";
+
+if (isMainThread) {
+ const server = http.createServer();
+ server.on("request", (req, res) => {
+ const startTime = hrtime.bigint();
+
+ const { searchParams, pathname } = new URL(
+ req.url,
+ `http://${req.headers.host}`
+ );
+ const num = parseInt(searchParams.get("num"));
+ res.on("finish", () => {
+ console.log(generateLogString(req, res, pathname, startTime));
+ });
+
+ const worker = new Worker("./24-http-prime-workers.mjs", {
+ workerData: { num: num },
+ });
+ worker.on("message", (result) => {
+ res.end(`${num} is prime? ${result}`);
+ });
+ });
+ server.listen(3000);
+
+ console.log(`Main thread (server) started with pid ${process.pid}`);
+} else {
+ const result = isPrime(workerData.num);
+ parentPort.postMessage(result);
+ process.exit();
+}
diff --git a/07-eloop/25-http-prime-workers-pool.mjs b/07-eloop/25-http-prime-workers-pool.mjs
new file mode 100644
index 0000000..6a786f7
--- /dev/null
+++ b/07-eloop/25-http-prime-workers-pool.mjs
@@ -0,0 +1,51 @@
+import http from "http";
+import process, { hrtime } from "process";
+import { generateLogString } from "./eloop-utils.mjs";
+import { isPrime } from "./cpu-intensive.mjs";
+import { Worker, isMainThread, parentPort } from "worker_threads";
+import { availableParallelism } from "os";
+
+const numCPUs = availableParallelism();
+
+if (isMainThread) {
+ const workerPool = [];
+ for (let i = 0; i < numCPUs; i++) {
+ workerPool.push(new Worker("./25-http-prime-workers-pool.mjs"));
+ }
+ console.log(`Created ${workerPool.length} worker threads`);
+
+ const server = http.createServer();
+ server.on("request", (req, res) => {
+ const startTime = hrtime.bigint();
+
+ const { searchParams, pathname } = new URL(
+ req.url,
+ `http://${req.headers.host}`
+ );
+ const num = parseInt(searchParams.get("num"));
+ res.on("finish", () => {
+ console.log(generateLogString(req, res, pathname, startTime));
+ });
+
+ const wk = workerPool.pop();
+ if (wk) {
+ const listener = (result) => {
+ wk.off("message", listener);
+ workerPool.push(wk);
+ res.end(`${num} is prime? ${result}`);
+ };
+ wk.on("message", listener);
+ wk.postMessage(num);
+ } else {
+ res.statusCode = 503;
+ res.end();
+ }
+ });
+ server.listen(3000);
+ console.log(`Main thread (server) started with pid ${process.pid}`);
+} else {
+ parentPort.on("message", (msg) => {
+ const result = isPrime(msg);
+ parentPort.postMessage(result);
+ });
+}
diff --git a/07-eloop/26-http-prime-workers-pool-2.mjs b/07-eloop/26-http-prime-workers-pool-2.mjs
new file mode 100644
index 0000000..add65a9
--- /dev/null
+++ b/07-eloop/26-http-prime-workers-pool-2.mjs
@@ -0,0 +1,43 @@
+import http from "http";
+import process, { hrtime } from "process";
+import { generateLogString } from "./eloop-utils.mjs";
+import { Worker } from "worker_threads";
+import { availableParallelism } from "os";
+
+const numCPUs = availableParallelism();
+
+const workerPool = [];
+for (let i = 0; i < numCPUs; i++) {
+ workerPool.push(new Worker("./isprime-worker.mjs"));
+}
+console.log(`Created ${workerPool.length} worker threads`);
+
+const server = http.createServer();
+server.on("request", (req, res) => {
+ const startTime = hrtime.bigint();
+
+ const { searchParams, pathname } = new URL(
+ req.url,
+ `http://${req.headers.host}`
+ );
+ const num = parseInt(searchParams.get("num"));
+ res.on("finish", () => {
+ console.log(generateLogString(req, res, pathname, startTime));
+ });
+
+ const wk = workerPool.pop();
+ if (wk) {
+ const listener = (result) => {
+ wk.off("message", listener);
+ workerPool.push(wk);
+ res.end(`${num} is prime? ${result}`);
+ };
+ wk.on("message", listener);
+ wk.postMessage(num);
+ } else {
+ res.statusCode = 503;
+ res.end();
+ }
+});
+server.listen(3000);
+console.log(`Main thread (server) started with pid ${process.pid}`);
diff --git a/07-eloop/cpu-intensive.mjs b/07-eloop/cpu-intensive.mjs
new file mode 100644
index 0000000..6b49250
--- /dev/null
+++ b/07-eloop/cpu-intensive.mjs
@@ -0,0 +1,51 @@
+function isPrime(num) {
+ if (num < 2) {
+ return false;
+ }
+ for (let i = 2; i < num; i++) {
+ if (num % i === 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function factorial(n) {
+ return n != 1 ? n * factorial(n - 1) : 1;
+}
+
+const PRIME_SMALL = 65537;
+const PRIME_MED = 999331;
+const PRIME_BIG = 39916801;
+const PRIME_HUGE = 2971215073;
+const PRIME_ENOR = 87178291199;
+
+function isPrimeAsync(num, cb) {
+ process.nextTick(() => {
+ if (num < 2) {
+ return cb(false);
+ }
+ isPrimeRecursive(num, 2, cb);
+ });
+}
+
+function isPrimeRecursive(n, i, cb) {
+ if (i >= n) {
+ return cb(true);
+ }
+ if (n % i === 0) {
+ return cb(false);
+ }
+ setImmediate(isPrimeRecursive.bind(null, n, i + 1, cb));
+}
+
+export {
+ isPrime,
+ factorial,
+ PRIME_SMALL,
+ PRIME_MED,
+ PRIME_BIG,
+ PRIME_HUGE,
+ PRIME_ENOR,
+ isPrimeAsync,
+};
diff --git a/07-eloop/eloop-utils.mjs b/07-eloop/eloop-utils.mjs
new file mode 100644
index 0000000..6faf9b3
--- /dev/null
+++ b/07-eloop/eloop-utils.mjs
@@ -0,0 +1,12 @@
+import { hrtime } from "process";
+
+function generateLogString(req, res, pathname, startTime) {
+ const endTime = hrtime.bigint();
+ const duration = (endTime - startTime) / BigInt(1e6);
+ const timestamp = new Date().toISOString();
+ const ip = req.socket.remoteAddress;
+ const httpDetails = `"${req.method} ${pathname}" ${res.statusCode}`;
+ return `${ip} - ${timestamp} ${httpDetails} - ${duration}ms`;
+}
+
+export { generateLogString };
diff --git a/07-eloop/isprime-child.mjs b/07-eloop/isprime-child.mjs
new file mode 100644
index 0000000..cfda533
--- /dev/null
+++ b/07-eloop/isprime-child.mjs
@@ -0,0 +1,7 @@
+import { isPrime } from "./cpu-intensive.mjs";
+
+process.on("message", (msg) => {
+ const result = isPrime(msg);
+ process.send(result);
+ process.exit();
+});
diff --git a/07-eloop/isprime-worker.mjs b/07-eloop/isprime-worker.mjs
new file mode 100644
index 0000000..0217b5f
--- /dev/null
+++ b/07-eloop/isprime-worker.mjs
@@ -0,0 +1,7 @@
+import { isPrime } from "./cpu-intensive.mjs";
+import { parentPort } from "worker_threads";
+
+parentPort.on("message", (msg) => {
+ const result = isPrime(msg);
+ parentPort.postMessage(result);
+});
diff --git a/07-eloop/isprime.c b/07-eloop/isprime.c
new file mode 100644
index 0000000..2183757
--- /dev/null
+++ b/07-eloop/isprime.c
@@ -0,0 +1,25 @@
+#include
+#include
+
+int main(int argc, char **argv)
+{
+ long i, num;
+ if (argc < 2)
+ {
+ return 1;
+ }
+
+ num = strtol(argv[1], NULL, 10);
+ if (num < 2)
+ {
+ return 1;
+ }
+ for (i = 2; i < num; ++i)
+ {
+ if (num % i == 0)
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/07-eloop/isprime.mjs b/07-eloop/isprime.mjs
new file mode 100755
index 0000000..e6e3691
--- /dev/null
+++ b/07-eloop/isprime.mjs
@@ -0,0 +1,16 @@
+#!/usr/bin/env node
+import { isPrime } from "./cpu-intensive.mjs";
+
+const num = parseInt(process.argv[2]);
+if (isNaN(num)) {
+ console.error(`${num} is not a number`);
+ process.exit(2);
+}
+
+if (isPrime(num)) {
+ console.log(`${num} is prime`);
+ process.exit(0);
+} else {
+ console.log(`${num} is not prime`);
+ process.exit(1);
+}
diff --git a/07-eloop/number.txt b/07-eloop/number.txt
new file mode 100644
index 0000000..01da44e
--- /dev/null
+++ b/07-eloop/number.txt
@@ -0,0 +1 @@
+39916801
\ No newline at end of file
diff --git a/08-pkg/01-check-strings.js b/08-pkg/01-check-strings.js
new file mode 100644
index 0000000..b13aae9
--- /dev/null
+++ b/08-pkg/01-check-strings.js
@@ -0,0 +1,13 @@
+const { isString } = require("./modules/is-string");
+
+const toCheck = [
+ "a string",
+ 2,
+ { prop: "I'm an object" },
+ function () {},
+ true,
+];
+
+for (const str of toCheck) {
+ console.log(isString(str));
+}
diff --git a/08-pkg/02-manipulate-strings.js b/08-pkg/02-manipulate-strings.js
new file mode 100644
index 0000000..8a4b81c
--- /dev/null
+++ b/08-pkg/02-manipulate-strings.js
@@ -0,0 +1,17 @@
+const { urlify } = require("./modules/string-utils");
+const { inspect } = require("util");
+
+const songs = [
+ "Guerrilla Radio",
+ "Killing in the name",
+ "Bullet in the head",
+ null,
+];
+
+for (const s of songs) {
+ try {
+ console.log(`${s} -> ${urlify(s)}`);
+ } catch (e) {
+ console.error(`Skipped non string value: ${inspect(s)}`);
+ }
+}
diff --git a/08-pkg/03-debugging-time.js b/08-pkg/03-debugging-time.js
new file mode 100644
index 0000000..0c91390
--- /dev/null
+++ b/08-pkg/03-debugging-time.js
@@ -0,0 +1,8 @@
+const debug = require("./modules/debug")("03");
+
+debug(`Script started at ${new Date().toISOString()}`);
+setTimeout(() => {
+ // doing something...
+
+ debug(`Script ended at ${new Date().toISOString()}`);
+}, 1000);
diff --git a/08-pkg/04-open-rad-map.js b/08-pkg/04-open-rad-map.js
new file mode 100644
index 0000000..99e4a49
--- /dev/null
+++ b/08-pkg/04-open-rad-map.js
@@ -0,0 +1,6 @@
+const debug = require("./modules/debug")("04");
+const open = require("./modules/open");
+
+debug("Script started");
+open("https://jciv.iidj.net/map/");
+debug("Function open called");
diff --git a/08-pkg/05-missing-mod.js b/08-pkg/05-missing-mod.js
new file mode 100644
index 0000000..cef38a6
--- /dev/null
+++ b/08-pkg/05-missing-mod.js
@@ -0,0 +1 @@
+require("./modules/my-mod");
diff --git a/08-pkg/06-shared-counter.js b/08-pkg/06-shared-counter.js
new file mode 100644
index 0000000..44b436d
--- /dev/null
+++ b/08-pkg/06-shared-counter.js
@@ -0,0 +1,7 @@
+const counter1 = require("./modules/shared-counter");
+const counter2 = require("./modules/shared-counter");
+
+counter1.inc();
+counter2.inc();
+
+console.log(counter1.val());
diff --git a/08-pkg/07-lines-counter.mjs b/08-pkg/07-lines-counter.mjs
new file mode 100644
index 0000000..990fe04
--- /dev/null
+++ b/08-pkg/07-lines-counter.mjs
@@ -0,0 +1,5 @@
+import { readFileLines, encoding } from "./modules/read-file-lines.mjs";
+
+const file = process.argv[2];
+const lines = await readFileLines(file);
+console.log(`${file} (${encoding}) is ${lines.length} lines long`);
diff --git a/08-pkg/08-numbered-echo.mjs b/08-pkg/08-numbered-echo.mjs
new file mode 100644
index 0000000..a5cff93
--- /dev/null
+++ b/08-pkg/08-numbered-echo.mjs
@@ -0,0 +1,6 @@
+import { readFileLines } from "./modules/read-file-lines.mjs";
+import printLines from "./modules/print-lines-numbers.mjs";
+
+const file = process.argv[2];
+const lines = await readFileLines(file);
+printLines(lines);
diff --git a/08-pkg/09-open-links.mjs b/08-pkg/09-open-links.mjs
new file mode 100644
index 0000000..ac0cd68
--- /dev/null
+++ b/08-pkg/09-open-links.mjs
@@ -0,0 +1,12 @@
+import printLines from "./modules/print-lines-numbers.mjs";
+import { readFileLines as readLinks } from "./modules/read-file-lines.mjs";
+import readNumber from "./modules/read-number.mjs";
+import open from "./modules/open/open.js";
+
+const linksFile = "./links.txt";
+const countFrom = 1;
+
+const links = await readLinks(linksFile);
+printLines(links, countFrom);
+const num = await readNumber("Digit a number and press Enter to open it: ");
+open(links[num - countFrom]);
diff --git a/08-pkg/10-feed-reader/.eslintrc b/08-pkg/10-feed-reader/.eslintrc
new file mode 100644
index 0000000..c8b7979
--- /dev/null
+++ b/08-pkg/10-feed-reader/.eslintrc
@@ -0,0 +1,10 @@
+{
+ "env": {
+ "node": true,
+ "es2022": true
+ },
+ "parserOptions": {
+ "sourceType": "module"
+ },
+ "extends": "eslint:recommended"
+}
diff --git a/08-pkg/10-feed-reader/.gitignore b/08-pkg/10-feed-reader/.gitignore
new file mode 100644
index 0000000..6dca520
--- /dev/null
+++ b/08-pkg/10-feed-reader/.gitignore
@@ -0,0 +1,2 @@
+feed-db.txt
+node_modules/
\ No newline at end of file
diff --git a/08-pkg/10-feed-reader/app.mjs b/08-pkg/10-feed-reader/app.mjs
new file mode 100644
index 0000000..84fd3a7
--- /dev/null
+++ b/08-pkg/10-feed-reader/app.mjs
@@ -0,0 +1,14 @@
+import printLines from "../modules/print-lines-numbers.mjs";
+import readNumber from "../modules/read-number.mjs";
+import open from "../modules/open/open.js";
+import getFeedEntries from "./get-feeds.mjs";
+
+const countFrom = 1;
+const feedUrl = process.argv[2];
+const entries = await getFeedEntries(feedUrl);
+const titles = entries.map((e) => e.title);
+
+printLines(titles, countFrom);
+const num = await readNumber("Digit a number and press Enter to open it: ");
+const selectedEntry = entries[num - countFrom];
+open(selectedEntry.link);
diff --git a/08-pkg/10-feed-reader/get-feeds.mjs b/08-pkg/10-feed-reader/get-feeds.mjs
new file mode 100644
index 0000000..132a346
--- /dev/null
+++ b/08-pkg/10-feed-reader/get-feeds.mjs
@@ -0,0 +1,11 @@
+import { parseFeed } from "htmlparser2";
+
+export default async function getFeedEntries(url, maxEntries = 5) {
+ const feedFetched = await fetch(url);
+ if (!feedFetched.ok) {
+ throw new Error("Feed fetch failed");
+ }
+ const feedContent = await feedFetched.text();
+ const { items } = parseFeed(feedContent);
+ return items.slice(0, maxEntries);
+}
diff --git a/08-pkg/10-feed-reader/package-lock.json b/08-pkg/10-feed-reader/package-lock.json
new file mode 100644
index 0000000..c6a72ed
--- /dev/null
+++ b/08-pkg/10-feed-reader/package-lock.json
@@ -0,0 +1,1216 @@
+{
+ "name": "10-feed-reader",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "10-feed-reader",
+ "dependencies": {
+ "htmlparser2": "^9.1.0"
+ },
+ "devDependencies": {
+ "eslint": "^8.56.0"
+ }
+ },
+ "node_modules/@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+ "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
+ "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.14",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.2",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
+ "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
+ "dev": true
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
+ "node_modules/acorn": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
+ "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.56.0",
+ "@humanwhocodes/config-array": "^0.11.13",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastq": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.9",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
+ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+ "dev": true
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz",
+ "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==",
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.1.0",
+ "entities": "^4.5.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+ "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
+ "dependencies": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/08-pkg/10-feed-reader/package.json b/08-pkg/10-feed-reader/package.json
new file mode 100644
index 0000000..d103943
--- /dev/null
+++ b/08-pkg/10-feed-reader/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "10-feed-reader",
+ "scripts": {
+ "start": "node app.mjs",
+ "node-blog": "node app.mjs https://nodejs.org/en/feed/blog.xml",
+ "start-debug": "NODE_DEBUG=* node app.mjs"
+ },
+ "dependencies": {
+ "htmlparser2": "^9.1.0"
+ },
+ "devDependencies": {
+ "eslint": "^8.56.0"
+ }
+}
diff --git a/08-pkg/10-feed-reader/wrong.mjs b/08-pkg/10-feed-reader/wrong.mjs
new file mode 100644
index 0000000..693ec3e
--- /dev/null
+++ b/08-pkg/10-feed-reader/wrong.mjs
@@ -0,0 +1 @@
+import wrong from "i-am-not-here";
diff --git a/08-pkg/links.txt b/08-pkg/links.txt
new file mode 100644
index 0000000..6a0bfb2
--- /dev/null
+++ b/08-pkg/links.txt
@@ -0,0 +1,4 @@
+https://jciv.iidj.net/map/
+https://remap.jrc.ec.europa.eu/
+https://vimeo.com/135580602
+https://nuclearsecrecy.com/nukemap/
\ No newline at end of file
diff --git a/08-pkg/modules/bookmark-utils.mjs b/08-pkg/modules/bookmark-utils.mjs
new file mode 100644
index 0000000..8a5bf24
--- /dev/null
+++ b/08-pkg/modules/bookmark-utils.mjs
@@ -0,0 +1 @@
+//TODO - unire tutto e fare tre export
diff --git a/08-pkg/modules/debug/index.js b/08-pkg/modules/debug/index.js
new file mode 100644
index 0000000..f41e67f
--- /dev/null
+++ b/08-pkg/modules/debug/index.js
@@ -0,0 +1,12 @@
+function debugFactory(tag) {
+ const enabled = process.env.DEBUG;
+
+ function debug(msg) {
+ if (!enabled) {
+ return;
+ }
+ console.log(`[${tag}] ${msg}`);
+ }
+ return debug;
+}
+module.exports = debugFactory;
diff --git a/08-pkg/modules/is-string.js b/08-pkg/modules/is-string.js
new file mode 100644
index 0000000..0d35585
--- /dev/null
+++ b/08-pkg/modules/is-string.js
@@ -0,0 +1,5 @@
+function isString(str) {
+ return typeof str === "string";
+}
+
+exports.isString = isString;
diff --git a/08-pkg/modules/open/open.js b/08-pkg/modules/open/open.js
new file mode 100644
index 0000000..f04445d
--- /dev/null
+++ b/08-pkg/modules/open/open.js
@@ -0,0 +1,43 @@
+const process = require("process");
+const childProcess = require("child_process");
+const { isString } = require("../is-string");
+const debug = require("../debug")("open");
+
+const { platform } = process;
+
+const platformCmds = {
+ darwin: "open",
+ linux: "xdg-open",
+ win32: "powershell.exe /c start",
+};
+
+debug("platform is " + platform);
+
+function open(target) {
+ if (!isString(target)) {
+ throw new TypeError("A target is required");
+ }
+ debug("target resource is " + target);
+
+ const command = platformCmds[platform];
+ if (!command) {
+ throw new Error(`${platform} is not supported.`);
+ }
+
+ const childProcessOptions = {
+ stdio: "ignore",
+ detached: true,
+ };
+
+ const spawnedProc = childProcess.spawn(
+ command,
+ [target],
+ childProcessOptions
+ );
+
+ spawnedProc.unref();
+ debug(`Spawned process PID is ${spawnedProc.pid}`);
+ return spawnedProc;
+}
+
+module.exports = open;
diff --git a/08-pkg/modules/open/package.json b/08-pkg/modules/open/package.json
new file mode 100644
index 0000000..563b016
--- /dev/null
+++ b/08-pkg/modules/open/package.json
@@ -0,0 +1,3 @@
+{
+ "main": "./open.js"
+}
diff --git a/08-pkg/modules/print-lines-numbers.mjs b/08-pkg/modules/print-lines-numbers.mjs
new file mode 100644
index 0000000..b410b74
--- /dev/null
+++ b/08-pkg/modules/print-lines-numbers.mjs
@@ -0,0 +1,7 @@
+function print(lines = [], countFrom = 0) {
+ for (let idx = 0; idx < lines.length; idx++) {
+ const line = lines[idx];
+ console.log(`[${countFrom + idx}] ${line}`);
+ }
+}
+export default print;
diff --git a/08-pkg/modules/read-file-lines.mjs b/08-pkg/modules/read-file-lines.mjs
new file mode 100644
index 0000000..57fecda
--- /dev/null
+++ b/08-pkg/modules/read-file-lines.mjs
@@ -0,0 +1,12 @@
+import { readFile } from "node:fs/promises";
+const encoding = "utf-8";
+const separator = "\n";
+
+async function readFileLines(filePath, sep = separator, enc = encoding) {
+ const fileContent = await readFile(filePath, { encoding: enc });
+ const lines = fileContent.split(sep);
+ return lines;
+}
+
+export { readFileLines, separator, encoding };
+export default readFileLines;
diff --git a/08-pkg/modules/read-number.mjs b/08-pkg/modules/read-number.mjs
new file mode 100644
index 0000000..361dd49
--- /dev/null
+++ b/08-pkg/modules/read-number.mjs
@@ -0,0 +1,9 @@
+import * as readline from "node:readline/promises";
+import { stdin as input, stdout as output } from "node:process";
+
+export default async function (prompt = "Insert a number") {
+ const rl = readline.createInterface({ input, output });
+ const answer = await rl.question(prompt);
+ rl.close();
+ return parseInt(answer);
+}
diff --git a/08-pkg/modules/shared-counter/counter.js b/08-pkg/modules/shared-counter/counter.js
new file mode 100644
index 0000000..ea9fdf8
--- /dev/null
+++ b/08-pkg/modules/shared-counter/counter.js
@@ -0,0 +1,5 @@
+let counter = 0;
+
+exports.inc = () => ++counter;
+exports.dec = () => --counter;
+exports.val = () => counter;
diff --git a/08-pkg/modules/shared-counter/package.json b/08-pkg/modules/shared-counter/package.json
new file mode 100644
index 0000000..3e94af7
--- /dev/null
+++ b/08-pkg/modules/shared-counter/package.json
@@ -0,0 +1,3 @@
+{
+ "main": "./counter.js"
+}
diff --git a/08-pkg/modules/string-utils/index.js b/08-pkg/modules/string-utils/index.js
new file mode 100644
index 0000000..8f8f7fe
--- /dev/null
+++ b/08-pkg/modules/string-utils/index.js
@@ -0,0 +1,13 @@
+function isString(str) {
+ return typeof str === "string";
+}
+
+function toUrl(str) {
+ if (!isString(str)) {
+ throw new TypeError("A string is required");
+ }
+ return str.toLowerCase().replace(/[^a-z0-9]+/g, "-");
+}
+
+exports.isString = isString;
+exports.urlify = toUrl;
diff --git a/09-express/01-apod/apod-api.mjs b/09-express/01-apod/apod-api.mjs
new file mode 100644
index 0000000..120a5e2
--- /dev/null
+++ b/09-express/01-apod/apod-api.mjs
@@ -0,0 +1,12 @@
+const API_URL = "https://api.nasa.gov/planetary/apod";
+const API_KEY = process.env.NASA_API_KEY || "DEMO_KEY";
+
+export async function getApodByDate(dateStr) {
+ const res = await fetch(`${API_URL}?date=${dateStr}&api_key=${API_KEY}`);
+ if (res.ok) {
+ const json = await res.json();
+ return json;
+ } else {
+ throw new Error(`API return a ${res.status} code`);
+ }
+}
diff --git a/09-express/01-apod/app_v1.mjs b/09-express/01-apod/app_v1.mjs
new file mode 100644
index 0000000..18f8244
--- /dev/null
+++ b/09-express/01-apod/app_v1.mjs
@@ -0,0 +1,27 @@
+import express from "express";
+const app = express();
+const port = 3000;
+
+app.get("/", (req, res) => {
+ res.send("Hello APOD!");
+});
+app.get("/about", (req, res) => {
+ res.send(`
+
+
+ About APOD
+
+
Astronomy Picture of the Day
+
+Astronomy Picture of the Day (APOD) is originated, written, coordinated,
+and edited since 1995 by Robert Nemiroff and Jerry Bonnell.
+The APOD archive contains the largest collection of annotated
+astronomical images on the internet.
+
+Astronomy Picture of the Day (APOD) is originated, written, coordinated,
+and edited since 1995 by Robert Nemiroff and Jerry Bonnell.
+The APOD archive contains the largest collection of annotated
+astronomical images on the internet.
+
+ Astronomy Picture of the Day (APOD) is originated, written,
+ coordinated, and edited since 1995 by Robert Nemiroff and Jerry
+ Bonnell. The APOD archive contains the largest collection of
+ annotated astronomical images on the internet.
+
+
+
\ No newline at end of file
diff --git a/09-express/01-apod/views/home.hbs b/09-express/01-apod/views/home.hbs
new file mode 100644
index 0000000..2455c71
--- /dev/null
+++ b/09-express/01-apod/views/home.hbs
@@ -0,0 +1,12 @@
+
+
+ {{title}}
+
+
+
+
+
{{title}}
+
+
+
+
\ No newline at end of file
diff --git a/09-express/01-apod/views2/404.hbs b/09-express/01-apod/views2/404.hbs
new file mode 100644
index 0000000..82d5852
--- /dev/null
+++ b/09-express/01-apod/views2/404.hbs
@@ -0,0 +1,2 @@
+
404
+
page not found :\
\ No newline at end of file
diff --git a/09-express/01-apod/views2/500.hbs b/09-express/01-apod/views2/500.hbs
new file mode 100644
index 0000000..430ad36
--- /dev/null
+++ b/09-express/01-apod/views2/500.hbs
@@ -0,0 +1 @@
+
Internal server error :\
\ No newline at end of file
diff --git a/09-express/01-apod/views2/about.hbs b/09-express/01-apod/views2/about.hbs
new file mode 100644
index 0000000..e51e198
--- /dev/null
+++ b/09-express/01-apod/views2/about.hbs
@@ -0,0 +1,7 @@
+
{{title}}
+
+ Astronomy Picture of the Day (APOD) is originated, written, coordinated, and
+ edited since 1995 by Robert Nemiroff and Jerry Bonnell. The APOD archive
+ contains the largest collection of annotated astronomical images on the
+ internet.
+
\ No newline at end of file
diff --git a/09-express/01-apod/views2/apod-full.hbs b/09-express/01-apod/views2/apod-full.hbs
new file mode 100644
index 0000000..328b2f4
--- /dev/null
+++ b/09-express/01-apod/views2/apod-full.hbs
@@ -0,0 +1,8 @@
+
{{title}}
+
Picture of {{date}}
+{{#if isImage}}
+
+{{else}}
+ Open video
+{{/if}}
+
{{description}}
\ No newline at end of file
diff --git a/09-express/01-apod/views2/apod-not-found.hbs b/09-express/01-apod/views2/apod-not-found.hbs
new file mode 100644
index 0000000..2edcd27
--- /dev/null
+++ b/09-express/01-apod/views2/apod-not-found.hbs
@@ -0,0 +1,2 @@
+
APOD not found
+
Please, try with another date
\ No newline at end of file
diff --git a/09-express/01-apod/views2/apod-pic.hbs b/09-express/01-apod/views2/apod-pic.hbs
new file mode 100644
index 0000000..ef13b97
--- /dev/null
+++ b/09-express/01-apod/views2/apod-pic.hbs
@@ -0,0 +1,2 @@
+
{{title}}
+
Picture of {{date}}
\ No newline at end of file
diff --git a/09-express/01-apod/views2/apod.hbs b/09-express/01-apod/views2/apod.hbs
new file mode 100644
index 0000000..70e7176
--- /dev/null
+++ b/09-express/01-apod/views2/apod.hbs
@@ -0,0 +1,4 @@
+
{{title}}
+
Picture of {{date}}
+
+
{{description}}
\ No newline at end of file
diff --git a/09-express/01-apod/views2/home.hbs b/09-express/01-apod/views2/home.hbs
new file mode 100644
index 0000000..ac77870
--- /dev/null
+++ b/09-express/01-apod/views2/home.hbs
@@ -0,0 +1,2 @@
+