update depedencies, changes to support updated authentication-module
authorJustin Wind <justin.wind+git@gmail.com>
Wed, 27 Mar 2024 00:08:59 +0000 (17:08 -0700)
committerJustin Wind <justin.wind+git@gmail.com>
Wed, 27 Mar 2024 23:28:41 +0000 (16:28 -0700)
38 files changed:
documentation/media/postgres-er.svg
documentation/media/sqlite-er.svg
package-lock.json
package.json
server.js
src/db/abstract.js
src/db/postgres/index.js
src/db/postgres/sql/authentication-update-credential.sql [new file with mode: 0644]
src/db/postgres/sql/authentication-update-otp-key.sql [new file with mode: 0644]
src/db/postgres/sql/authentication-upsert.sql
src/db/postgres/sql/schema/1.2.0/apply.sql [new file with mode: 0644]
src/db/postgres/sql/schema/1.2.0/er.dot [new file with mode: 0644]
src/db/postgres/sql/schema/1.2.0/revert.sql [new file with mode: 0644]
src/db/sqlite/index.js
src/db/sqlite/sql/authentication-get.sql
src/db/sqlite/sql/authentication-update-credential.sql [new file with mode: 0644]
src/db/sqlite/sql/authentication-update-otp-key.sql [new file with mode: 0644]
src/db/sqlite/sql/authentication-upsert.sql
src/db/sqlite/sql/schema/1.2.0/apply.sql [new file with mode: 0644]
src/db/sqlite/sql/schema/1.2.0/er.dot [new file with mode: 0644]
src/db/sqlite/sql/schema/1.2.0/revert.sql [new file with mode: 0644]
src/logger/data-sanitizers.js
src/service.js
src/template/admin-html.js
src/template/admin-maintenance-html.js
src/template/admin-ticket-html.js
src/template/authorization-error-html.js
src/template/authorization-request-html.js
src/template/root-html.js
src/template/template-helper.js
static/theme.css
test/src/db/integration.js
test/src/db/postgres.js
test/src/db/sqlite.js
test/src/logger.js
test/src/service.js
test/src/template/template-helper.js
test/stub-db.js

index ac8658e4be78f93ff519221d14b5384a21da411f..0d3027d9ca63ef7aef520c648d99b78991c266d6 100644 (file)
 <!-- Generated by graphviz version 2.50.0 (20211204.2007)
  -->
 <!-- Title: indieAutherERD Pages: 1 -->
-<svg width="669pt" height="668pt"
- viewBox="0.00 0.00 669.00 668.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 664)">
+<svg width="669pt" height="669pt"
+ viewBox="0.00 0.00 669.00 669.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 665)">
 <title>indieAutherERD</title>
-<polygon fill="white" stroke="transparent" points="-4,4 -4,-664 665,-664 665,4 -4,4"/>
-<text text-anchor="middle" x="330.5" y="-635.2" font-family="Times,serif" font-size="26.00">IndieAuther Entity&#45;Relations</text>
-<text text-anchor="middle" x="330.5" y="-606.2" font-family="Times,serif" font-size="26.00">Postgres</text>
-<text text-anchor="middle" x="330.5" y="-577.2" font-family="Times,serif" font-size="26.00">Schema 1.1.0</text>
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-665 665,-665 665,4 -4,4"/>
+<text text-anchor="middle" x="330.5" y="-636.2" font-family="Times,serif" font-size="26.00">IndieAuther Entity&#45;Relations</text>
+<text text-anchor="middle" x="330.5" y="-607.2" font-family="Times,serif" font-size="26.00">Postgres</text>
+<text text-anchor="middle" x="330.5" y="-578.2" font-family="Times,serif" font-size="26.00">Schema 1.1.0</text>
 <!-- redeemed_ticket -->
 <g id="node1" class="node">
 <title>redeemed_ticket</title>
-<polygon fill="lightblue" stroke="transparent" points="0.5,-186.5 0.5,-209.5 159.5,-209.5 159.5,-186.5 0.5,-186.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="1.5,-187.5 1.5,-208.5 158.5,-208.5 158.5,-187.5 1.5,-187.5"/>
-<text text-anchor="start" x="4.5" y="-194.3" font-family="Times,serif" font-size="14.00">REDEEMED_TICKET</text>
-<polygon fill="none" stroke="black" points="0.5,-165.5 0.5,-186.5 159.5,-186.5 159.5,-165.5 0.5,-165.5"/>
-<text text-anchor="start" x="49.5" y="-172.3" font-family="Times,serif" font-size="14.00">ticket_id</text>
-<polygon fill="none" stroke="black" points="0.5,-144.5 0.5,-165.5 159.5,-165.5 159.5,-144.5 0.5,-144.5"/>
-<text text-anchor="start" x="52.5" y="-151.3" font-family="Times,serif" font-size="14.00">created</text>
-<polygon fill="none" stroke="black" points="0.5,-123.5 0.5,-144.5 159.5,-144.5 159.5,-123.5 0.5,-123.5"/>
-<text text-anchor="start" x="54" y="-130.3" font-family="Times,serif" font-size="14.00">subject</text>
-<polygon fill="none" stroke="black" points="0.5,-102.5 0.5,-123.5 159.5,-123.5 159.5,-102.5 0.5,-102.5"/>
-<text text-anchor="start" x="48.5" y="-109.3" font-family="Times,serif" font-size="14.00">resource</text>
-<polygon fill="none" stroke="black" points="0.5,-81.5 0.5,-102.5 159.5,-102.5 159.5,-81.5 0.5,-81.5"/>
-<text text-anchor="start" x="70" y="-88.3" font-family="Times,serif" font-size="14.00">iss</text>
-<polygon fill="none" stroke="black" points="0.5,-60.5 0.5,-81.5 159.5,-81.5 159.5,-60.5 0.5,-60.5"/>
-<text text-anchor="start" x="60" y="-67.3" font-family="Times,serif" font-size="14.00">token</text>
-<polygon fill="none" stroke="black" points="0.5,-39.5 0.5,-60.5 159.5,-60.5 159.5,-39.5 0.5,-39.5"/>
-<text text-anchor="start" x="59.5" y="-46.3" font-family="Times,serif" font-size="14.00">ticket</text>
-<polygon fill="none" stroke="black" points="0.5,-18.5 0.5,-39.5 159.5,-39.5 159.5,-18.5 0.5,-18.5"/>
-<text text-anchor="start" x="45" y="-25.3" font-family="Times,serif" font-size="14.00">published</text>
+<polygon fill="lightblue" stroke="transparent" points="0.5,-167.5 0.5,-190.5 159.5,-190.5 159.5,-167.5 0.5,-167.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="1.5,-168.5 1.5,-189.5 158.5,-189.5 158.5,-168.5 1.5,-168.5"/>
+<text text-anchor="start" x="4.5" y="-175.3" font-family="Times,serif" font-size="14.00">REDEEMED_TICKET</text>
+<polygon fill="none" stroke="black" points="0.5,-146.5 0.5,-167.5 159.5,-167.5 159.5,-146.5 0.5,-146.5"/>
+<text text-anchor="start" x="49.5" y="-153.3" font-family="Times,serif" font-size="14.00">ticket_id</text>
+<polygon fill="none" stroke="black" points="0.5,-125.5 0.5,-146.5 159.5,-146.5 159.5,-125.5 0.5,-125.5"/>
+<text text-anchor="start" x="52.5" y="-132.3" font-family="Times,serif" font-size="14.00">created</text>
+<polygon fill="none" stroke="black" points="0.5,-104.5 0.5,-125.5 159.5,-125.5 159.5,-104.5 0.5,-104.5"/>
+<text text-anchor="start" x="54" y="-111.3" font-family="Times,serif" font-size="14.00">subject</text>
+<polygon fill="none" stroke="black" points="0.5,-83.5 0.5,-104.5 159.5,-104.5 159.5,-83.5 0.5,-83.5"/>
+<text text-anchor="start" x="48.5" y="-90.3" font-family="Times,serif" font-size="14.00">resource</text>
+<polygon fill="none" stroke="black" points="0.5,-62.5 0.5,-83.5 159.5,-83.5 159.5,-62.5 0.5,-62.5"/>
+<text text-anchor="start" x="70" y="-69.3" font-family="Times,serif" font-size="14.00">iss</text>
+<polygon fill="none" stroke="black" points="0.5,-41.5 0.5,-62.5 159.5,-62.5 159.5,-41.5 0.5,-41.5"/>
+<text text-anchor="start" x="60" y="-48.3" font-family="Times,serif" font-size="14.00">token</text>
+<polygon fill="none" stroke="black" points="0.5,-20.5 0.5,-41.5 159.5,-41.5 159.5,-20.5 0.5,-20.5"/>
+<text text-anchor="start" x="59.5" y="-27.3" font-family="Times,serif" font-size="14.00">ticket</text>
+<polygon fill="none" stroke="black" points="0.5,0.5 0.5,-20.5 159.5,-20.5 159.5,0.5 0.5,0.5"/>
+<text text-anchor="start" x="45" y="-6.3" font-family="Times,serif" font-size="14.00">published</text>
 </g>
 <!-- token -->
 <g id="node2" class="node">
 <title>token</title>
-<polygon fill="lightblue" stroke="transparent" points="375.5,-501.5 375.5,-524.5 501.5,-524.5 501.5,-501.5 375.5,-501.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="376.5,-502.5 376.5,-523.5 500.5,-523.5 500.5,-502.5 376.5,-502.5"/>
-<text text-anchor="start" x="411.5" y="-509.3" font-family="Times,serif" font-size="14.00">TOKEN</text>
-<polygon fill="none" stroke="black" points="375.5,-480.5 375.5,-501.5 501.5,-501.5 501.5,-480.5 375.5,-480.5"/>
-<text text-anchor="start" x="411.5" y="-487.3" font-family="Times,serif" font-size="14.00">code_id</text>
-<polygon fill="none" stroke="black" points="375.5,-459.5 375.5,-480.5 501.5,-480.5 501.5,-459.5 375.5,-459.5"/>
-<text text-anchor="start" x="405" y="-466.3" font-family="Times,serif" font-size="14.00">profile_id</text>
-<polygon fill="none" stroke="black" points="375.5,-438.5 375.5,-459.5 501.5,-459.5 501.5,-438.5 375.5,-438.5"/>
-<text text-anchor="start" x="411" y="-445.3" font-family="Times,serif" font-size="14.00">created</text>
-<polygon fill="none" stroke="black" points="375.5,-417.5 375.5,-438.5 501.5,-438.5 501.5,-417.5 375.5,-417.5"/>
-<text text-anchor="start" x="412" y="-424.3" font-family="Times,serif" font-size="14.00">expires</text>
-<polygon fill="none" stroke="black" points="375.5,-396.5 375.5,-417.5 501.5,-417.5 501.5,-396.5 375.5,-396.5"/>
-<text text-anchor="start" x="382.5" y="-403.3" font-family="Times,serif" font-size="14.00">refresh_expires</text>
-<polygon fill="none" stroke="black" points="375.5,-375.5 375.5,-396.5 501.5,-396.5 501.5,-375.5 375.5,-375.5"/>
-<text text-anchor="start" x="403.5" y="-382.3" font-family="Times,serif" font-size="14.00">refreshed</text>
-<polygon fill="none" stroke="black" points="375.5,-354.5 375.5,-375.5 501.5,-375.5 501.5,-354.5 375.5,-354.5"/>
-<text text-anchor="start" x="408" y="-361.3" font-family="Times,serif" font-size="14.00">duration</text>
-<polygon fill="none" stroke="black" points="375.5,-333.5 375.5,-354.5 501.5,-354.5 501.5,-333.5 375.5,-333.5"/>
-<text text-anchor="start" x="378.5" y="-340.3" font-family="Times,serif" font-size="14.00">refresh_duration</text>
-<polygon fill="none" stroke="black" points="375.5,-312.5 375.5,-333.5 501.5,-333.5 501.5,-312.5 375.5,-312.5"/>
-<text text-anchor="start" x="389" y="-319.3" font-family="Times,serif" font-size="14.00">refresh_count</text>
-<polygon fill="none" stroke="black" points="375.5,-291.5 375.5,-312.5 501.5,-312.5 501.5,-291.5 375.5,-291.5"/>
-<text text-anchor="start" x="400.5" y="-298.3" font-family="Times,serif" font-size="14.00">is_revoked</text>
-<polygon fill="none" stroke="black" points="375.5,-270.5 375.5,-291.5 501.5,-291.5 501.5,-270.5 375.5,-270.5"/>
-<text text-anchor="start" x="409" y="-277.3" font-family="Times,serif" font-size="14.00">is_token</text>
-<polygon fill="none" stroke="black" points="375.5,-249.5 375.5,-270.5 501.5,-270.5 501.5,-249.5 375.5,-249.5"/>
-<text text-anchor="start" x="408" y="-256.3" font-family="Times,serif" font-size="14.00">client_id</text>
-<polygon fill="none" stroke="black" points="375.5,-228.5 375.5,-249.5 501.5,-249.5 501.5,-228.5 375.5,-228.5"/>
-<text text-anchor="start" x="407" y="-235.3" font-family="Times,serif" font-size="14.00">resource</text>
-<polygon fill="none" stroke="black" points="375.5,-207.5 375.5,-228.5 501.5,-228.5 501.5,-207.5 375.5,-207.5"/>
-<text text-anchor="start" x="396" y="-214.3" font-family="Times,serif" font-size="14.00">profile_data</text>
+<polygon fill="lightblue" stroke="transparent" points="375.5,-502.5 375.5,-525.5 501.5,-525.5 501.5,-502.5 375.5,-502.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="376.5,-503.5 376.5,-524.5 500.5,-524.5 500.5,-503.5 376.5,-503.5"/>
+<text text-anchor="start" x="411.5" y="-510.3" font-family="Times,serif" font-size="14.00">TOKEN</text>
+<polygon fill="none" stroke="black" points="375.5,-481.5 375.5,-502.5 501.5,-502.5 501.5,-481.5 375.5,-481.5"/>
+<text text-anchor="start" x="411.5" y="-488.3" font-family="Times,serif" font-size="14.00">code_id</text>
+<polygon fill="none" stroke="black" points="375.5,-460.5 375.5,-481.5 501.5,-481.5 501.5,-460.5 375.5,-460.5"/>
+<text text-anchor="start" x="405" y="-467.3" font-family="Times,serif" font-size="14.00">profile_id</text>
+<polygon fill="none" stroke="black" points="375.5,-439.5 375.5,-460.5 501.5,-460.5 501.5,-439.5 375.5,-439.5"/>
+<text text-anchor="start" x="411" y="-446.3" font-family="Times,serif" font-size="14.00">created</text>
+<polygon fill="none" stroke="black" points="375.5,-418.5 375.5,-439.5 501.5,-439.5 501.5,-418.5 375.5,-418.5"/>
+<text text-anchor="start" x="412" y="-425.3" font-family="Times,serif" font-size="14.00">expires</text>
+<polygon fill="none" stroke="black" points="375.5,-397.5 375.5,-418.5 501.5,-418.5 501.5,-397.5 375.5,-397.5"/>
+<text text-anchor="start" x="382.5" y="-404.3" font-family="Times,serif" font-size="14.00">refresh_expires</text>
+<polygon fill="none" stroke="black" points="375.5,-376.5 375.5,-397.5 501.5,-397.5 501.5,-376.5 375.5,-376.5"/>
+<text text-anchor="start" x="403.5" y="-383.3" font-family="Times,serif" font-size="14.00">refreshed</text>
+<polygon fill="none" stroke="black" points="375.5,-355.5 375.5,-376.5 501.5,-376.5 501.5,-355.5 375.5,-355.5"/>
+<text text-anchor="start" x="408" y="-362.3" font-family="Times,serif" font-size="14.00">duration</text>
+<polygon fill="none" stroke="black" points="375.5,-334.5 375.5,-355.5 501.5,-355.5 501.5,-334.5 375.5,-334.5"/>
+<text text-anchor="start" x="378.5" y="-341.3" font-family="Times,serif" font-size="14.00">refresh_duration</text>
+<polygon fill="none" stroke="black" points="375.5,-313.5 375.5,-334.5 501.5,-334.5 501.5,-313.5 375.5,-313.5"/>
+<text text-anchor="start" x="389" y="-320.3" font-family="Times,serif" font-size="14.00">refresh_count</text>
+<polygon fill="none" stroke="black" points="375.5,-292.5 375.5,-313.5 501.5,-313.5 501.5,-292.5 375.5,-292.5"/>
+<text text-anchor="start" x="400.5" y="-299.3" font-family="Times,serif" font-size="14.00">is_revoked</text>
+<polygon fill="none" stroke="black" points="375.5,-271.5 375.5,-292.5 501.5,-292.5 501.5,-271.5 375.5,-271.5"/>
+<text text-anchor="start" x="409" y="-278.3" font-family="Times,serif" font-size="14.00">is_token</text>
+<polygon fill="none" stroke="black" points="375.5,-250.5 375.5,-271.5 501.5,-271.5 501.5,-250.5 375.5,-250.5"/>
+<text text-anchor="start" x="408" y="-257.3" font-family="Times,serif" font-size="14.00">client_id</text>
+<polygon fill="none" stroke="black" points="375.5,-229.5 375.5,-250.5 501.5,-250.5 501.5,-229.5 375.5,-229.5"/>
+<text text-anchor="start" x="407" y="-236.3" font-family="Times,serif" font-size="14.00">resource</text>
+<polygon fill="none" stroke="black" points="375.5,-208.5 375.5,-229.5 501.5,-229.5 501.5,-208.5 375.5,-208.5"/>
+<text text-anchor="start" x="396" y="-215.3" font-family="Times,serif" font-size="14.00">profile_data</text>
 </g>
 <!-- token_scope -->
 <g id="node5" class="node">
 <title>token_scope</title>
-<polygon fill="lightblue" stroke="transparent" points="541,-315.5 541,-338.5 661,-338.5 661,-315.5 541,-315.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="542,-316.5 542,-337.5 660,-337.5 660,-316.5 542,-316.5"/>
-<text text-anchor="start" x="545" y="-323.3" font-family="Times,serif" font-size="14.00">TOKEN_SCOPE</text>
-<polygon fill="none" stroke="black" points="541,-294.5 541,-315.5 661,-315.5 661,-294.5 541,-294.5"/>
-<text text-anchor="start" x="574" y="-301.3" font-family="Times,serif" font-size="14.00">code_id</text>
-<polygon fill="none" stroke="black" points="541,-273.5 541,-294.5 661,-294.5 661,-273.5 541,-273.5"/>
-<text text-anchor="start" x="570.5" y="-280.3" font-family="Times,serif" font-size="14.00">scope_id</text>
+<polygon fill="lightblue" stroke="transparent" points="541,-316.5 541,-339.5 661,-339.5 661,-316.5 541,-316.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="542,-317.5 542,-338.5 660,-338.5 660,-317.5 542,-317.5"/>
+<text text-anchor="start" x="545" y="-324.3" font-family="Times,serif" font-size="14.00">TOKEN_SCOPE</text>
+<polygon fill="none" stroke="black" points="541,-295.5 541,-316.5 661,-316.5 661,-295.5 541,-295.5"/>
+<text text-anchor="start" x="574" y="-302.3" font-family="Times,serif" font-size="14.00">code_id</text>
+<polygon fill="none" stroke="black" points="541,-274.5 541,-295.5 661,-295.5 661,-274.5 541,-274.5"/>
+<text text-anchor="start" x="570.5" y="-281.3" font-family="Times,serif" font-size="14.00">scope_id</text>
 </g>
 <!-- token&#45;&gt;token_scope -->
 <g id="edge2" class="edge">
 <title>token:pk_code_id&#45;&gt;token_scope:fk_code_id</title>
-<path fill="none" stroke="black" d="M502.5,-491.5C583.54,-491.5 467.5,-320.92 530.83,-305.6"/>
-<polygon fill="black" stroke="black" points="531.06,-305.57 541.48,-308.97 536.03,-305.04 541,-304.5 541,-304.5 541,-304.5 536.03,-305.04 540.52,-300.03 531.06,-305.57 531.06,-305.57"/>
+<path fill="none" stroke="black" d="M502.5,-492.5C583.54,-492.5 467.5,-321.92 530.83,-306.6"/>
+<polygon fill="black" stroke="black" points="531.06,-306.57 541.48,-309.97 536.03,-306.04 541,-305.5 541,-305.5 541,-305.5 536.03,-306.04 540.52,-301.03 531.06,-306.57 531.06,-306.57"/>
 </g>
 <!-- profile -->
 <g id="node3" class="node">
 <title>profile</title>
-<polygon fill="lightblue" stroke="transparent" points="219.5,-355.5 219.5,-378.5 312.5,-378.5 312.5,-355.5 219.5,-355.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="220.5,-356.5 220.5,-377.5 311.5,-377.5 311.5,-356.5 220.5,-356.5"/>
-<text text-anchor="start" x="232.5" y="-363.3" font-family="Times,serif" font-size="14.00">PROFILE</text>
-<polygon fill="none" stroke="black" points="219.5,-334.5 219.5,-355.5 312.5,-355.5 312.5,-334.5 219.5,-334.5"/>
-<text text-anchor="start" x="232.5" y="-341.3" font-family="Times,serif" font-size="14.00">profile_id</text>
-<polygon fill="none" stroke="black" points="219.5,-313.5 219.5,-334.5 312.5,-334.5 312.5,-313.5 219.5,-313.5"/>
-<text text-anchor="start" x="222.5" y="-320.3" font-family="Times,serif" font-size="14.00">identifier_id</text>
-<polygon fill="none" stroke="black" points="219.5,-292.5 219.5,-313.5 312.5,-313.5 312.5,-292.5 219.5,-292.5"/>
-<text text-anchor="start" x="242.5" y="-299.3" font-family="Times,serif" font-size="14.00">profile</text>
+<polygon fill="lightblue" stroke="transparent" points="219.5,-356.5 219.5,-379.5 312.5,-379.5 312.5,-356.5 219.5,-356.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="220.5,-357.5 220.5,-378.5 311.5,-378.5 311.5,-357.5 220.5,-357.5"/>
+<text text-anchor="start" x="232.5" y="-364.3" font-family="Times,serif" font-size="14.00">PROFILE</text>
+<polygon fill="none" stroke="black" points="219.5,-335.5 219.5,-356.5 312.5,-356.5 312.5,-335.5 219.5,-335.5"/>
+<text text-anchor="start" x="232.5" y="-342.3" font-family="Times,serif" font-size="14.00">profile_id</text>
+<polygon fill="none" stroke="black" points="219.5,-314.5 219.5,-335.5 312.5,-335.5 312.5,-314.5 219.5,-314.5"/>
+<text text-anchor="start" x="222.5" y="-321.3" font-family="Times,serif" font-size="14.00">identifier_id</text>
+<polygon fill="none" stroke="black" points="219.5,-293.5 219.5,-314.5 312.5,-314.5 312.5,-293.5 219.5,-293.5"/>
+<text text-anchor="start" x="242.5" y="-300.3" font-family="Times,serif" font-size="14.00">profile</text>
 </g>
 <!-- profile&#45;&gt;token -->
 <g id="edge1" class="edge">
 <title>profile:pk_profile_id&#45;&gt;token:fk_profile_id</title>
-<path fill="none" stroke="black" d="M313.5,-345.5C371.57,-345.5 319.81,-455.82 364.58,-469.18"/>
-<polygon fill="black" stroke="black" points="364.59,-469.18 373.91,-474.96 369.54,-469.84 374.5,-470.5 374.5,-470.5 374.5,-470.5 369.54,-469.84 375.09,-466.04 364.59,-469.18 364.59,-469.18"/>
+<path fill="none" stroke="black" d="M313.5,-346.5C371.57,-346.5 319.81,-456.82 364.58,-470.18"/>
+<polygon fill="black" stroke="black" points="364.59,-470.18 373.91,-475.96 369.54,-470.84 374.5,-471.5 374.5,-471.5 374.5,-471.5 369.54,-470.84 375.09,-467.04 364.59,-470.18 364.59,-470.18"/>
 </g>
 <!-- profile_scope -->
 <g id="node7" class="node">
 <title>profile_scope</title>
-<polygon fill="lightblue" stroke="transparent" points="372.5,-166.5 372.5,-189.5 505.5,-189.5 505.5,-166.5 372.5,-166.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="373.5,-167.5 373.5,-188.5 504.5,-188.5 504.5,-167.5 373.5,-167.5"/>
-<text text-anchor="start" x="376.5" y="-174.3" font-family="Times,serif" font-size="14.00">PROFILE_SCOPE</text>
-<polygon fill="none" stroke="black" points="372.5,-145.5 372.5,-166.5 505.5,-166.5 505.5,-145.5 372.5,-145.5"/>
-<text text-anchor="start" x="405.5" y="-152.3" font-family="Times,serif" font-size="14.00">profile_id</text>
-<polygon fill="none" stroke="black" points="372.5,-124.5 372.5,-145.5 505.5,-145.5 505.5,-124.5 372.5,-124.5"/>
-<text text-anchor="start" x="408.5" y="-131.3" font-family="Times,serif" font-size="14.00">scope_id</text>
+<polygon fill="lightblue" stroke="transparent" points="372.5,-167.5 372.5,-190.5 505.5,-190.5 505.5,-167.5 372.5,-167.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="373.5,-168.5 373.5,-189.5 504.5,-189.5 504.5,-168.5 373.5,-168.5"/>
+<text text-anchor="start" x="376.5" y="-175.3" font-family="Times,serif" font-size="14.00">PROFILE_SCOPE</text>
+<polygon fill="none" stroke="black" points="372.5,-146.5 372.5,-167.5 505.5,-167.5 505.5,-146.5 372.5,-146.5"/>
+<text text-anchor="start" x="405.5" y="-153.3" font-family="Times,serif" font-size="14.00">profile_id</text>
+<polygon fill="none" stroke="black" points="372.5,-125.5 372.5,-146.5 505.5,-146.5 505.5,-125.5 372.5,-125.5"/>
+<text text-anchor="start" x="408.5" y="-132.3" font-family="Times,serif" font-size="14.00">scope_id</text>
 </g>
 <!-- profile&#45;&gt;profile_scope -->
 <g id="edge5" class="edge">
 <title>profile:pk_profile_id&#45;&gt;profile_scope:fk_profile_id</title>
-<path fill="none" stroke="black" d="M313.5,-345.5C398.06,-345.5 293.42,-171.48 361.72,-156.52"/>
-<polygon fill="black" stroke="black" points="362.05,-156.49 372.45,-159.98 367.02,-155.99 372,-155.5 372,-155.5 372,-155.5 367.02,-155.99 371.55,-151.02 362.05,-156.49 362.05,-156.49"/>
+<path fill="none" stroke="black" d="M313.5,-346.5C398.06,-346.5 293.42,-172.48 361.72,-157.52"/>
+<polygon fill="black" stroke="black" points="362.05,-157.49 372.45,-160.98 367.02,-156.99 372,-156.5 372,-156.5 372,-156.5 367.02,-156.99 371.55,-152.02 362.05,-157.49 362.05,-157.49"/>
 </g>
 <!-- scope -->
 <g id="node4" class="node">
 <title>scope</title>
-<polygon fill="lightblue" stroke="transparent" points="195.5,-125.5 195.5,-148.5 336.5,-148.5 336.5,-125.5 195.5,-125.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="196.5,-126.5 196.5,-147.5 335.5,-147.5 335.5,-126.5 196.5,-126.5"/>
-<text text-anchor="start" x="240" y="-133.3" font-family="Times,serif" font-size="14.00">SCOPE</text>
-<polygon fill="none" stroke="black" points="195.5,-104.5 195.5,-125.5 336.5,-125.5 336.5,-104.5 195.5,-104.5"/>
-<text text-anchor="start" x="235.5" y="-111.3" font-family="Times,serif" font-size="14.00">scope_id</text>
-<polygon fill="none" stroke="black" points="195.5,-83.5 195.5,-104.5 336.5,-104.5 336.5,-83.5 195.5,-83.5"/>
-<text text-anchor="start" x="245.5" y="-90.3" font-family="Times,serif" font-size="14.00">scope</text>
-<polygon fill="none" stroke="black" points="195.5,-62.5 195.5,-83.5 336.5,-83.5 336.5,-62.5 195.5,-62.5"/>
-<text text-anchor="start" x="225.5" y="-69.3" font-family="Times,serif" font-size="14.00">description</text>
-<polygon fill="none" stroke="black" points="195.5,-41.5 195.5,-62.5 336.5,-62.5 336.5,-41.5 195.5,-41.5"/>
-<text text-anchor="start" x="226.5" y="-48.3" font-family="Times,serif" font-size="14.00">application</text>
-<polygon fill="none" stroke="black" points="195.5,-20.5 195.5,-41.5 336.5,-41.5 336.5,-20.5 195.5,-20.5"/>
-<text text-anchor="start" x="217.5" y="-27.3" font-family="Times,serif" font-size="14.00">is_permanent</text>
-<polygon fill="none" stroke="black" points="195.5,0.5 195.5,-20.5 336.5,-20.5 336.5,0.5 195.5,0.5"/>
-<text text-anchor="start" x="198.5" y="-6.3" font-family="Times,serif" font-size="14.00">is_manually_added</text>
+<polygon fill="lightblue" stroke="transparent" points="195.5,-126.5 195.5,-149.5 336.5,-149.5 336.5,-126.5 195.5,-126.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="196.5,-127.5 196.5,-148.5 335.5,-148.5 335.5,-127.5 196.5,-127.5"/>
+<text text-anchor="start" x="240" y="-134.3" font-family="Times,serif" font-size="14.00">SCOPE</text>
+<polygon fill="none" stroke="black" points="195.5,-105.5 195.5,-126.5 336.5,-126.5 336.5,-105.5 195.5,-105.5"/>
+<text text-anchor="start" x="235.5" y="-112.3" font-family="Times,serif" font-size="14.00">scope_id</text>
+<polygon fill="none" stroke="black" points="195.5,-84.5 195.5,-105.5 336.5,-105.5 336.5,-84.5 195.5,-84.5"/>
+<text text-anchor="start" x="245.5" y="-91.3" font-family="Times,serif" font-size="14.00">scope</text>
+<polygon fill="none" stroke="black" points="195.5,-63.5 195.5,-84.5 336.5,-84.5 336.5,-63.5 195.5,-63.5"/>
+<text text-anchor="start" x="225.5" y="-70.3" font-family="Times,serif" font-size="14.00">description</text>
+<polygon fill="none" stroke="black" points="195.5,-42.5 195.5,-63.5 336.5,-63.5 336.5,-42.5 195.5,-42.5"/>
+<text text-anchor="start" x="226.5" y="-49.3" font-family="Times,serif" font-size="14.00">application</text>
+<polygon fill="none" stroke="black" points="195.5,-21.5 195.5,-42.5 336.5,-42.5 336.5,-21.5 195.5,-21.5"/>
+<text text-anchor="start" x="217.5" y="-28.3" font-family="Times,serif" font-size="14.00">is_permanent</text>
+<polygon fill="none" stroke="black" points="195.5,-0.5 195.5,-21.5 336.5,-21.5 336.5,-0.5 195.5,-0.5"/>
+<text text-anchor="start" x="198.5" y="-7.3" font-family="Times,serif" font-size="14.00">is_manually_added</text>
 </g>
 <!-- scope&#45;&gt;token_scope -->
 <g id="edge3" class="edge">
 <title>scope:pk_scope_id&#45;&gt;token_scope:fk_scope_id</title>
-<path fill="none" stroke="black" d="M336,-115.5C411.11,-115.5 446.59,-68.28 505,-115.5 561.48,-161.16 474,-271.95 531.03,-282.66"/>
-<polygon fill="black" stroke="black" points="531.03,-282.66 540.62,-287.98 536.02,-283.08 541,-283.5 541,-283.5 541,-283.5 536.02,-283.08 541.38,-279.02 531.03,-282.66 531.03,-282.66"/>
+<path fill="none" stroke="black" d="M336,-116.5C411.11,-116.5 446.59,-69.28 505,-116.5 561.48,-162.16 474,-272.95 531.03,-283.66"/>
+<polygon fill="black" stroke="black" points="531.03,-283.66 540.62,-288.98 536.02,-284.08 541,-284.5 541,-284.5 541,-284.5 536.02,-284.08 541.38,-280.02 531.03,-283.66 531.03,-283.66"/>
 </g>
 <!-- scope&#45;&gt;profile_scope -->
 <g id="edge6" class="edge">
 <title>scope:pk_scope_id&#45;&gt;profile_scope:fk_scope_id</title>
-<path fill="none" stroke="black" d="M336,-115.5C349.99,-115.5 353.05,-126.87 362.09,-132.02"/>
-<polygon fill="black" stroke="black" points="362.3,-132.07 370.91,-138.86 367.15,-133.28 372,-134.5 372,-134.5 372,-134.5 367.15,-133.28 373.09,-130.14 362.3,-132.07 362.3,-132.07"/>
+<path fill="none" stroke="black" d="M336,-116.5C349.99,-116.5 353.05,-127.87 362.09,-133.02"/>
+<polygon fill="black" stroke="black" points="362.3,-133.07 370.91,-139.86 367.15,-134.28 372,-135.5 372,-135.5 372,-135.5 367.15,-134.28 373.09,-131.14 362.3,-133.07 362.3,-133.07"/>
 </g>
 <!-- authentication -->
 <g id="node6" class="node">
 <title>authentication</title>
-<polygon fill="lightblue" stroke="transparent" points="6.5,-333.5 6.5,-356.5 152.5,-356.5 152.5,-333.5 6.5,-333.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="7.5,-334.5 7.5,-355.5 151.5,-355.5 151.5,-334.5 7.5,-334.5"/>
-<text text-anchor="start" x="10.5" y="-341.3" font-family="Times,serif" font-size="14.00">AUTHENTICATION</text>
-<polygon fill="none" stroke="black" points="6.5,-312.5 6.5,-333.5 152.5,-333.5 152.5,-312.5 6.5,-312.5"/>
-<text text-anchor="start" x="36" y="-319.3" font-family="Times,serif" font-size="14.00">identifier_id</text>
-<polygon fill="none" stroke="black" points="6.5,-291.5 6.5,-312.5 152.5,-312.5 152.5,-291.5 6.5,-291.5"/>
-<text text-anchor="start" x="52" y="-298.3" font-family="Times,serif" font-size="14.00">created</text>
-<polygon fill="none" stroke="black" points="6.5,-270.5 6.5,-291.5 152.5,-291.5 152.5,-270.5 6.5,-270.5"/>
-<text text-anchor="start" x="13" y="-277.3" font-family="Times,serif" font-size="14.00">last_authenticated</text>
-<polygon fill="none" stroke="black" points="6.5,-249.5 6.5,-270.5 152.5,-270.5 152.5,-249.5 6.5,-249.5"/>
-<text text-anchor="start" x="46" y="-256.3" font-family="Times,serif" font-size="14.00">identifier</text>
-<polygon fill="none" stroke="black" points="6.5,-228.5 6.5,-249.5 152.5,-249.5 152.5,-228.5 6.5,-228.5"/>
-<text text-anchor="start" x="43" y="-235.3" font-family="Times,serif" font-size="14.00">credential</text>
+<polygon fill="lightblue" stroke="transparent" points="6.5,-334.5 6.5,-357.5 152.5,-357.5 152.5,-334.5 6.5,-334.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="7.5,-335.5 7.5,-356.5 151.5,-356.5 151.5,-335.5 7.5,-335.5"/>
+<text text-anchor="start" x="10.5" y="-342.3" font-family="Times,serif" font-size="14.00">AUTHENTICATION</text>
+<polygon fill="none" stroke="black" points="6.5,-313.5 6.5,-334.5 152.5,-334.5 152.5,-313.5 6.5,-313.5"/>
+<text text-anchor="start" x="36" y="-320.3" font-family="Times,serif" font-size="14.00">identifier_id</text>
+<polygon fill="none" stroke="black" points="6.5,-292.5 6.5,-313.5 152.5,-313.5 152.5,-292.5 6.5,-292.5"/>
+<text text-anchor="start" x="52" y="-299.3" font-family="Times,serif" font-size="14.00">created</text>
+<polygon fill="none" stroke="black" points="6.5,-271.5 6.5,-292.5 152.5,-292.5 152.5,-271.5 6.5,-271.5"/>
+<text text-anchor="start" x="13" y="-278.3" font-family="Times,serif" font-size="14.00">last_authenticated</text>
+<polygon fill="none" stroke="black" points="6.5,-250.5 6.5,-271.5 152.5,-271.5 152.5,-250.5 6.5,-250.5"/>
+<text text-anchor="start" x="46" y="-257.3" font-family="Times,serif" font-size="14.00">identifier</text>
+<polygon fill="none" stroke="black" points="6.5,-229.5 6.5,-250.5 152.5,-250.5 152.5,-229.5 6.5,-229.5"/>
+<text text-anchor="start" x="43" y="-236.3" font-family="Times,serif" font-size="14.00">credential</text>
+<polygon fill="none" stroke="black" points="6.5,-208.5 6.5,-229.5 152.5,-229.5 152.5,-208.5 6.5,-208.5"/>
+<text text-anchor="start" x="52" y="-215.3" font-family="Times,serif" font-size="14.00">otp_key</text>
 </g>
 <!-- authentication&#45;&gt;profile -->
 <g id="edge4" class="edge">
 <title>authentication:pk_identifier_id&#45;&gt;profile:fk_identifier_id</title>
-<path fill="none" stroke="black" d="M153.5,-323.5C178.66,-323.5 187.39,-323.5 208.32,-323.5"/>
-<polygon fill="black" stroke="black" points="208.5,-323.5 218.5,-328 213.5,-323.5 218.5,-323.5 218.5,-323.5 218.5,-323.5 213.5,-323.5 218.5,-319 208.5,-323.5 208.5,-323.5"/>
+<path fill="none" stroke="black" d="M153.5,-324.5C178.66,-324.5 187.39,-324.5 208.32,-324.5"/>
+<polygon fill="black" stroke="black" points="208.5,-324.5 218.5,-329 213.5,-324.5 218.5,-324.5 218.5,-324.5 218.5,-324.5 213.5,-324.5 218.5,-320 208.5,-324.5 208.5,-324.5"/>
 </g>
 <!-- resource -->
 <g id="node8" class="node">
 <title>resource</title>
-<polygon fill="lightblue" stroke="transparent" points="32.5,-458.5 32.5,-481.5 126.5,-481.5 126.5,-458.5 32.5,-458.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="33.5,-459.5 33.5,-480.5 125.5,-480.5 125.5,-459.5 33.5,-459.5"/>
-<text text-anchor="start" x="36.5" y="-466.3" font-family="Times,serif" font-size="14.00">RESOURCE</text>
-<polygon fill="none" stroke="black" points="32.5,-437.5 32.5,-458.5 126.5,-458.5 126.5,-437.5 32.5,-437.5"/>
-<text text-anchor="start" x="38" y="-444.3" font-family="Times,serif" font-size="14.00">resource_id</text>
-<polygon fill="none" stroke="black" points="32.5,-416.5 32.5,-437.5 126.5,-437.5 126.5,-416.5 32.5,-416.5"/>
-<text text-anchor="start" x="39" y="-423.3" font-family="Times,serif" font-size="14.00">description</text>
-<polygon fill="none" stroke="black" points="32.5,-395.5 32.5,-416.5 126.5,-416.5 126.5,-395.5 32.5,-395.5"/>
-<text text-anchor="start" x="52" y="-402.3" font-family="Times,serif" font-size="14.00">created</text>
-<polygon fill="none" stroke="black" points="32.5,-374.5 32.5,-395.5 126.5,-395.5 126.5,-374.5 32.5,-374.5"/>
-<text text-anchor="start" x="57" y="-381.3" font-family="Times,serif" font-size="14.00">secret</text>
+<polygon fill="lightblue" stroke="transparent" points="32.5,-459.5 32.5,-482.5 126.5,-482.5 126.5,-459.5 32.5,-459.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="33.5,-460.5 33.5,-481.5 125.5,-481.5 125.5,-460.5 33.5,-460.5"/>
+<text text-anchor="start" x="36.5" y="-467.3" font-family="Times,serif" font-size="14.00">RESOURCE</text>
+<polygon fill="none" stroke="black" points="32.5,-438.5 32.5,-459.5 126.5,-459.5 126.5,-438.5 32.5,-438.5"/>
+<text text-anchor="start" x="38" y="-445.3" font-family="Times,serif" font-size="14.00">resource_id</text>
+<polygon fill="none" stroke="black" points="32.5,-417.5 32.5,-438.5 126.5,-438.5 126.5,-417.5 32.5,-417.5"/>
+<text text-anchor="start" x="39" y="-424.3" font-family="Times,serif" font-size="14.00">description</text>
+<polygon fill="none" stroke="black" points="32.5,-396.5 32.5,-417.5 126.5,-417.5 126.5,-396.5 32.5,-396.5"/>
+<text text-anchor="start" x="52" y="-403.3" font-family="Times,serif" font-size="14.00">created</text>
+<polygon fill="none" stroke="black" points="32.5,-375.5 32.5,-396.5 126.5,-396.5 126.5,-375.5 32.5,-375.5"/>
+<text text-anchor="start" x="57" y="-382.3" font-family="Times,serif" font-size="14.00">secret</text>
 </g>
 <!-- almanac -->
 <g id="node9" class="node">
 <title>almanac</title>
-<polygon fill="lightblue" stroke="transparent" points="37.5,-541.5 37.5,-564.5 121.5,-564.5 121.5,-541.5 37.5,-541.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="38.5,-542.5 38.5,-563.5 120.5,-563.5 120.5,-542.5 38.5,-542.5"/>
-<text text-anchor="start" x="41.5" y="-549.3" font-family="Times,serif" font-size="14.00">ALMANAC</text>
-<polygon fill="none" stroke="black" points="37.5,-520.5 37.5,-541.5 121.5,-541.5 121.5,-520.5 37.5,-520.5"/>
-<text text-anchor="start" x="59.5" y="-527.3" font-family="Times,serif" font-size="14.00">event</text>
-<polygon fill="none" stroke="black" points="37.5,-499.5 37.5,-520.5 121.5,-520.5 121.5,-499.5 37.5,-499.5"/>
-<text text-anchor="start" x="63.5" y="-506.3" font-family="Times,serif" font-size="14.00">date</text>
+<polygon fill="lightblue" stroke="transparent" points="37.5,-542.5 37.5,-565.5 121.5,-565.5 121.5,-542.5 37.5,-542.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="38.5,-543.5 38.5,-564.5 120.5,-564.5 120.5,-543.5 38.5,-543.5"/>
+<text text-anchor="start" x="41.5" y="-550.3" font-family="Times,serif" font-size="14.00">ALMANAC</text>
+<polygon fill="none" stroke="black" points="37.5,-521.5 37.5,-542.5 121.5,-542.5 121.5,-521.5 37.5,-521.5"/>
+<text text-anchor="start" x="59.5" y="-528.3" font-family="Times,serif" font-size="14.00">event</text>
+<polygon fill="none" stroke="black" points="37.5,-500.5 37.5,-521.5 121.5,-521.5 121.5,-500.5 37.5,-500.5"/>
+<text text-anchor="start" x="63.5" y="-507.3" font-family="Times,serif" font-size="14.00">date</text>
 </g>
 </g>
 </svg>
index ca8ee1c83c8c755209d09b5aa32be649149d3edd..9935e635a4e9f15d500a19b0b63ef88ec10efdee 100644 (file)
 <!-- Generated by graphviz version 2.50.0 (20211204.2007)
  -->
 <!-- Title: indieAutherERD Pages: 1 -->
-<svg width="669pt" height="639pt"
- viewBox="0.00 0.00 669.00 639.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 635)">
+<svg width="669pt" height="640pt"
+ viewBox="0.00 0.00 669.00 640.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 636)">
 <title>indieAutherERD</title>
-<polygon fill="white" stroke="transparent" points="-4,4 -4,-635 665,-635 665,4 -4,4"/>
-<text text-anchor="middle" x="330.5" y="-606.2" font-family="Times,serif" font-size="26.00">IndieAuther Entity&#45;RelationsSQLite</text>
-<text text-anchor="middle" x="330.5" y="-577.2" font-family="Times,serif" font-size="26.00">Schema 1.1.0</text>
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-636 665,-636 665,4 -4,4"/>
+<text text-anchor="middle" x="330.5" y="-607.2" font-family="Times,serif" font-size="26.00">IndieAuther Entity&#45;RelationsSQLite</text>
+<text text-anchor="middle" x="330.5" y="-578.2" font-family="Times,serif" font-size="26.00">Schema 1.1.0</text>
 <!-- redeemed_ticket -->
 <g id="node1" class="node">
 <title>redeemed_ticket</title>
-<polygon fill="lightblue" stroke="transparent" points="0.5,-186.5 0.5,-209.5 159.5,-209.5 159.5,-186.5 0.5,-186.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="1.5,-187.5 1.5,-208.5 158.5,-208.5 158.5,-187.5 1.5,-187.5"/>
-<text text-anchor="start" x="4.5" y="-194.3" font-family="Times,serif" font-size="14.00">REDEEMED_TICKET</text>
-<polygon fill="none" stroke="black" points="0.5,-165.5 0.5,-186.5 159.5,-186.5 159.5,-165.5 0.5,-165.5"/>
-<text text-anchor="start" x="49.5" y="-172.3" font-family="Times,serif" font-size="14.00">ticket_id</text>
-<polygon fill="none" stroke="black" points="0.5,-144.5 0.5,-165.5 159.5,-165.5 159.5,-144.5 0.5,-144.5"/>
-<text text-anchor="start" x="52.5" y="-151.3" font-family="Times,serif" font-size="14.00">created</text>
-<polygon fill="none" stroke="black" points="0.5,-123.5 0.5,-144.5 159.5,-144.5 159.5,-123.5 0.5,-123.5"/>
-<text text-anchor="start" x="54" y="-130.3" font-family="Times,serif" font-size="14.00">subject</text>
-<polygon fill="none" stroke="black" points="0.5,-102.5 0.5,-123.5 159.5,-123.5 159.5,-102.5 0.5,-102.5"/>
-<text text-anchor="start" x="48.5" y="-109.3" font-family="Times,serif" font-size="14.00">resource</text>
-<polygon fill="none" stroke="black" points="0.5,-81.5 0.5,-102.5 159.5,-102.5 159.5,-81.5 0.5,-81.5"/>
-<text text-anchor="start" x="70" y="-88.3" font-family="Times,serif" font-size="14.00">iss</text>
-<polygon fill="none" stroke="black" points="0.5,-60.5 0.5,-81.5 159.5,-81.5 159.5,-60.5 0.5,-60.5"/>
-<text text-anchor="start" x="60" y="-67.3" font-family="Times,serif" font-size="14.00">token</text>
-<polygon fill="none" stroke="black" points="0.5,-39.5 0.5,-60.5 159.5,-60.5 159.5,-39.5 0.5,-39.5"/>
-<text text-anchor="start" x="59.5" y="-46.3" font-family="Times,serif" font-size="14.00">ticket</text>
-<polygon fill="none" stroke="black" points="0.5,-18.5 0.5,-39.5 159.5,-39.5 159.5,-18.5 0.5,-18.5"/>
-<text text-anchor="start" x="45" y="-25.3" font-family="Times,serif" font-size="14.00">published</text>
+<polygon fill="lightblue" stroke="transparent" points="0.5,-167.5 0.5,-190.5 159.5,-190.5 159.5,-167.5 0.5,-167.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="1.5,-168.5 1.5,-189.5 158.5,-189.5 158.5,-168.5 1.5,-168.5"/>
+<text text-anchor="start" x="4.5" y="-175.3" font-family="Times,serif" font-size="14.00">REDEEMED_TICKET</text>
+<polygon fill="none" stroke="black" points="0.5,-146.5 0.5,-167.5 159.5,-167.5 159.5,-146.5 0.5,-146.5"/>
+<text text-anchor="start" x="49.5" y="-153.3" font-family="Times,serif" font-size="14.00">ticket_id</text>
+<polygon fill="none" stroke="black" points="0.5,-125.5 0.5,-146.5 159.5,-146.5 159.5,-125.5 0.5,-125.5"/>
+<text text-anchor="start" x="52.5" y="-132.3" font-family="Times,serif" font-size="14.00">created</text>
+<polygon fill="none" stroke="black" points="0.5,-104.5 0.5,-125.5 159.5,-125.5 159.5,-104.5 0.5,-104.5"/>
+<text text-anchor="start" x="54" y="-111.3" font-family="Times,serif" font-size="14.00">subject</text>
+<polygon fill="none" stroke="black" points="0.5,-83.5 0.5,-104.5 159.5,-104.5 159.5,-83.5 0.5,-83.5"/>
+<text text-anchor="start" x="48.5" y="-90.3" font-family="Times,serif" font-size="14.00">resource</text>
+<polygon fill="none" stroke="black" points="0.5,-62.5 0.5,-83.5 159.5,-83.5 159.5,-62.5 0.5,-62.5"/>
+<text text-anchor="start" x="70" y="-69.3" font-family="Times,serif" font-size="14.00">iss</text>
+<polygon fill="none" stroke="black" points="0.5,-41.5 0.5,-62.5 159.5,-62.5 159.5,-41.5 0.5,-41.5"/>
+<text text-anchor="start" x="60" y="-48.3" font-family="Times,serif" font-size="14.00">token</text>
+<polygon fill="none" stroke="black" points="0.5,-20.5 0.5,-41.5 159.5,-41.5 159.5,-20.5 0.5,-20.5"/>
+<text text-anchor="start" x="59.5" y="-27.3" font-family="Times,serif" font-size="14.00">ticket</text>
+<polygon fill="none" stroke="black" points="0.5,0.5 0.5,-20.5 159.5,-20.5 159.5,0.5 0.5,0.5"/>
+<text text-anchor="start" x="45" y="-6.3" font-family="Times,serif" font-size="14.00">published</text>
 </g>
 <!-- token -->
 <g id="node2" class="node">
 <title>token</title>
-<polygon fill="lightblue" stroke="transparent" points="375.5,-501.5 375.5,-524.5 501.5,-524.5 501.5,-501.5 375.5,-501.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="376.5,-502.5 376.5,-523.5 500.5,-523.5 500.5,-502.5 376.5,-502.5"/>
-<text text-anchor="start" x="411.5" y="-509.3" font-family="Times,serif" font-size="14.00">TOKEN</text>
-<polygon fill="none" stroke="black" points="375.5,-480.5 375.5,-501.5 501.5,-501.5 501.5,-480.5 375.5,-480.5"/>
-<text text-anchor="start" x="411.5" y="-487.3" font-family="Times,serif" font-size="14.00">code_id</text>
-<polygon fill="none" stroke="black" points="375.5,-459.5 375.5,-480.5 501.5,-480.5 501.5,-459.5 375.5,-459.5"/>
-<text text-anchor="start" x="405" y="-466.3" font-family="Times,serif" font-size="14.00">profile_id</text>
-<polygon fill="none" stroke="black" points="375.5,-438.5 375.5,-459.5 501.5,-459.5 501.5,-438.5 375.5,-438.5"/>
-<text text-anchor="start" x="411" y="-445.3" font-family="Times,serif" font-size="14.00">created</text>
-<polygon fill="none" stroke="black" points="375.5,-417.5 375.5,-438.5 501.5,-438.5 501.5,-417.5 375.5,-417.5"/>
-<text text-anchor="start" x="412" y="-424.3" font-family="Times,serif" font-size="14.00">expires</text>
-<polygon fill="none" stroke="black" points="375.5,-396.5 375.5,-417.5 501.5,-417.5 501.5,-396.5 375.5,-396.5"/>
-<text text-anchor="start" x="382.5" y="-403.3" font-family="Times,serif" font-size="14.00">refresh_expires</text>
-<polygon fill="none" stroke="black" points="375.5,-375.5 375.5,-396.5 501.5,-396.5 501.5,-375.5 375.5,-375.5"/>
-<text text-anchor="start" x="403.5" y="-382.3" font-family="Times,serif" font-size="14.00">refreshed</text>
-<polygon fill="none" stroke="black" points="375.5,-354.5 375.5,-375.5 501.5,-375.5 501.5,-354.5 375.5,-354.5"/>
-<text text-anchor="start" x="408" y="-361.3" font-family="Times,serif" font-size="14.00">duration</text>
-<polygon fill="none" stroke="black" points="375.5,-333.5 375.5,-354.5 501.5,-354.5 501.5,-333.5 375.5,-333.5"/>
-<text text-anchor="start" x="378.5" y="-340.3" font-family="Times,serif" font-size="14.00">refresh_duration</text>
-<polygon fill="none" stroke="black" points="375.5,-312.5 375.5,-333.5 501.5,-333.5 501.5,-312.5 375.5,-312.5"/>
-<text text-anchor="start" x="389" y="-319.3" font-family="Times,serif" font-size="14.00">refresh_count</text>
-<polygon fill="none" stroke="black" points="375.5,-291.5 375.5,-312.5 501.5,-312.5 501.5,-291.5 375.5,-291.5"/>
-<text text-anchor="start" x="400.5" y="-298.3" font-family="Times,serif" font-size="14.00">is_revoked</text>
-<polygon fill="none" stroke="black" points="375.5,-270.5 375.5,-291.5 501.5,-291.5 501.5,-270.5 375.5,-270.5"/>
-<text text-anchor="start" x="409" y="-277.3" font-family="Times,serif" font-size="14.00">is_token</text>
-<polygon fill="none" stroke="black" points="375.5,-249.5 375.5,-270.5 501.5,-270.5 501.5,-249.5 375.5,-249.5"/>
-<text text-anchor="start" x="408" y="-256.3" font-family="Times,serif" font-size="14.00">client_id</text>
-<polygon fill="none" stroke="black" points="375.5,-228.5 375.5,-249.5 501.5,-249.5 501.5,-228.5 375.5,-228.5"/>
-<text text-anchor="start" x="407" y="-235.3" font-family="Times,serif" font-size="14.00">resource</text>
-<polygon fill="none" stroke="black" points="375.5,-207.5 375.5,-228.5 501.5,-228.5 501.5,-207.5 375.5,-207.5"/>
-<text text-anchor="start" x="396" y="-214.3" font-family="Times,serif" font-size="14.00">profile_data</text>
+<polygon fill="lightblue" stroke="transparent" points="375.5,-502.5 375.5,-525.5 501.5,-525.5 501.5,-502.5 375.5,-502.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="376.5,-503.5 376.5,-524.5 500.5,-524.5 500.5,-503.5 376.5,-503.5"/>
+<text text-anchor="start" x="411.5" y="-510.3" font-family="Times,serif" font-size="14.00">TOKEN</text>
+<polygon fill="none" stroke="black" points="375.5,-481.5 375.5,-502.5 501.5,-502.5 501.5,-481.5 375.5,-481.5"/>
+<text text-anchor="start" x="411.5" y="-488.3" font-family="Times,serif" font-size="14.00">code_id</text>
+<polygon fill="none" stroke="black" points="375.5,-460.5 375.5,-481.5 501.5,-481.5 501.5,-460.5 375.5,-460.5"/>
+<text text-anchor="start" x="405" y="-467.3" font-family="Times,serif" font-size="14.00">profile_id</text>
+<polygon fill="none" stroke="black" points="375.5,-439.5 375.5,-460.5 501.5,-460.5 501.5,-439.5 375.5,-439.5"/>
+<text text-anchor="start" x="411" y="-446.3" font-family="Times,serif" font-size="14.00">created</text>
+<polygon fill="none" stroke="black" points="375.5,-418.5 375.5,-439.5 501.5,-439.5 501.5,-418.5 375.5,-418.5"/>
+<text text-anchor="start" x="412" y="-425.3" font-family="Times,serif" font-size="14.00">expires</text>
+<polygon fill="none" stroke="black" points="375.5,-397.5 375.5,-418.5 501.5,-418.5 501.5,-397.5 375.5,-397.5"/>
+<text text-anchor="start" x="382.5" y="-404.3" font-family="Times,serif" font-size="14.00">refresh_expires</text>
+<polygon fill="none" stroke="black" points="375.5,-376.5 375.5,-397.5 501.5,-397.5 501.5,-376.5 375.5,-376.5"/>
+<text text-anchor="start" x="403.5" y="-383.3" font-family="Times,serif" font-size="14.00">refreshed</text>
+<polygon fill="none" stroke="black" points="375.5,-355.5 375.5,-376.5 501.5,-376.5 501.5,-355.5 375.5,-355.5"/>
+<text text-anchor="start" x="408" y="-362.3" font-family="Times,serif" font-size="14.00">duration</text>
+<polygon fill="none" stroke="black" points="375.5,-334.5 375.5,-355.5 501.5,-355.5 501.5,-334.5 375.5,-334.5"/>
+<text text-anchor="start" x="378.5" y="-341.3" font-family="Times,serif" font-size="14.00">refresh_duration</text>
+<polygon fill="none" stroke="black" points="375.5,-313.5 375.5,-334.5 501.5,-334.5 501.5,-313.5 375.5,-313.5"/>
+<text text-anchor="start" x="389" y="-320.3" font-family="Times,serif" font-size="14.00">refresh_count</text>
+<polygon fill="none" stroke="black" points="375.5,-292.5 375.5,-313.5 501.5,-313.5 501.5,-292.5 375.5,-292.5"/>
+<text text-anchor="start" x="400.5" y="-299.3" font-family="Times,serif" font-size="14.00">is_revoked</text>
+<polygon fill="none" stroke="black" points="375.5,-271.5 375.5,-292.5 501.5,-292.5 501.5,-271.5 375.5,-271.5"/>
+<text text-anchor="start" x="409" y="-278.3" font-family="Times,serif" font-size="14.00">is_token</text>
+<polygon fill="none" stroke="black" points="375.5,-250.5 375.5,-271.5 501.5,-271.5 501.5,-250.5 375.5,-250.5"/>
+<text text-anchor="start" x="408" y="-257.3" font-family="Times,serif" font-size="14.00">client_id</text>
+<polygon fill="none" stroke="black" points="375.5,-229.5 375.5,-250.5 501.5,-250.5 501.5,-229.5 375.5,-229.5"/>
+<text text-anchor="start" x="407" y="-236.3" font-family="Times,serif" font-size="14.00">resource</text>
+<polygon fill="none" stroke="black" points="375.5,-208.5 375.5,-229.5 501.5,-229.5 501.5,-208.5 375.5,-208.5"/>
+<text text-anchor="start" x="396" y="-215.3" font-family="Times,serif" font-size="14.00">profile_data</text>
 </g>
 <!-- token_scope -->
 <g id="node5" class="node">
 <title>token_scope</title>
-<polygon fill="lightblue" stroke="transparent" points="541,-315.5 541,-338.5 661,-338.5 661,-315.5 541,-315.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="542,-316.5 542,-337.5 660,-337.5 660,-316.5 542,-316.5"/>
-<text text-anchor="start" x="545" y="-323.3" font-family="Times,serif" font-size="14.00">TOKEN_SCOPE</text>
-<polygon fill="none" stroke="black" points="541,-294.5 541,-315.5 661,-315.5 661,-294.5 541,-294.5"/>
-<text text-anchor="start" x="574" y="-301.3" font-family="Times,serif" font-size="14.00">code_id</text>
-<polygon fill="none" stroke="black" points="541,-273.5 541,-294.5 661,-294.5 661,-273.5 541,-273.5"/>
-<text text-anchor="start" x="570.5" y="-280.3" font-family="Times,serif" font-size="14.00">scope_id</text>
+<polygon fill="lightblue" stroke="transparent" points="541,-316.5 541,-339.5 661,-339.5 661,-316.5 541,-316.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="542,-317.5 542,-338.5 660,-338.5 660,-317.5 542,-317.5"/>
+<text text-anchor="start" x="545" y="-324.3" font-family="Times,serif" font-size="14.00">TOKEN_SCOPE</text>
+<polygon fill="none" stroke="black" points="541,-295.5 541,-316.5 661,-316.5 661,-295.5 541,-295.5"/>
+<text text-anchor="start" x="574" y="-302.3" font-family="Times,serif" font-size="14.00">code_id</text>
+<polygon fill="none" stroke="black" points="541,-274.5 541,-295.5 661,-295.5 661,-274.5 541,-274.5"/>
+<text text-anchor="start" x="570.5" y="-281.3" font-family="Times,serif" font-size="14.00">scope_id</text>
 </g>
 <!-- token&#45;&gt;token_scope -->
 <g id="edge2" class="edge">
 <title>token:pk_code_id&#45;&gt;token_scope:fk_code_id</title>
-<path fill="none" stroke="black" d="M502.5,-491.5C583.54,-491.5 467.5,-320.92 530.83,-305.6"/>
-<polygon fill="black" stroke="black" points="531.06,-305.57 541.48,-308.97 536.03,-305.04 541,-304.5 541,-304.5 541,-304.5 536.03,-305.04 540.52,-300.03 531.06,-305.57 531.06,-305.57"/>
+<path fill="none" stroke="black" d="M502.5,-492.5C583.54,-492.5 467.5,-321.92 530.83,-306.6"/>
+<polygon fill="black" stroke="black" points="531.06,-306.57 541.48,-309.97 536.03,-306.04 541,-305.5 541,-305.5 541,-305.5 536.03,-306.04 540.52,-301.03 531.06,-306.57 531.06,-306.57"/>
 </g>
 <!-- profile -->
 <g id="node3" class="node">
 <title>profile</title>
-<polygon fill="lightblue" stroke="transparent" points="219.5,-355.5 219.5,-378.5 312.5,-378.5 312.5,-355.5 219.5,-355.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="220.5,-356.5 220.5,-377.5 311.5,-377.5 311.5,-356.5 220.5,-356.5"/>
-<text text-anchor="start" x="232.5" y="-363.3" font-family="Times,serif" font-size="14.00">PROFILE</text>
-<polygon fill="none" stroke="black" points="219.5,-334.5 219.5,-355.5 312.5,-355.5 312.5,-334.5 219.5,-334.5"/>
-<text text-anchor="start" x="232.5" y="-341.3" font-family="Times,serif" font-size="14.00">profile_id</text>
-<polygon fill="none" stroke="black" points="219.5,-313.5 219.5,-334.5 312.5,-334.5 312.5,-313.5 219.5,-313.5"/>
-<text text-anchor="start" x="222.5" y="-320.3" font-family="Times,serif" font-size="14.00">identifier_id</text>
-<polygon fill="none" stroke="black" points="219.5,-292.5 219.5,-313.5 312.5,-313.5 312.5,-292.5 219.5,-292.5"/>
-<text text-anchor="start" x="242.5" y="-299.3" font-family="Times,serif" font-size="14.00">profile</text>
+<polygon fill="lightblue" stroke="transparent" points="219.5,-356.5 219.5,-379.5 312.5,-379.5 312.5,-356.5 219.5,-356.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="220.5,-357.5 220.5,-378.5 311.5,-378.5 311.5,-357.5 220.5,-357.5"/>
+<text text-anchor="start" x="232.5" y="-364.3" font-family="Times,serif" font-size="14.00">PROFILE</text>
+<polygon fill="none" stroke="black" points="219.5,-335.5 219.5,-356.5 312.5,-356.5 312.5,-335.5 219.5,-335.5"/>
+<text text-anchor="start" x="232.5" y="-342.3" font-family="Times,serif" font-size="14.00">profile_id</text>
+<polygon fill="none" stroke="black" points="219.5,-314.5 219.5,-335.5 312.5,-335.5 312.5,-314.5 219.5,-314.5"/>
+<text text-anchor="start" x="222.5" y="-321.3" font-family="Times,serif" font-size="14.00">identifier_id</text>
+<polygon fill="none" stroke="black" points="219.5,-293.5 219.5,-314.5 312.5,-314.5 312.5,-293.5 219.5,-293.5"/>
+<text text-anchor="start" x="242.5" y="-300.3" font-family="Times,serif" font-size="14.00">profile</text>
 </g>
 <!-- profile&#45;&gt;token -->
 <g id="edge1" class="edge">
 <title>profile:pk_profile_id&#45;&gt;token:fk_profile_id</title>
-<path fill="none" stroke="black" d="M313.5,-345.5C371.57,-345.5 319.81,-455.82 364.58,-469.18"/>
-<polygon fill="black" stroke="black" points="364.59,-469.18 373.91,-474.96 369.54,-469.84 374.5,-470.5 374.5,-470.5 374.5,-470.5 369.54,-469.84 375.09,-466.04 364.59,-469.18 364.59,-469.18"/>
+<path fill="none" stroke="black" d="M313.5,-346.5C371.57,-346.5 319.81,-456.82 364.58,-470.18"/>
+<polygon fill="black" stroke="black" points="364.59,-470.18 373.91,-475.96 369.54,-470.84 374.5,-471.5 374.5,-471.5 374.5,-471.5 369.54,-470.84 375.09,-467.04 364.59,-470.18 364.59,-470.18"/>
 </g>
 <!-- profile_scope -->
 <g id="node7" class="node">
 <title>profile_scope</title>
-<polygon fill="lightblue" stroke="transparent" points="372.5,-166.5 372.5,-189.5 505.5,-189.5 505.5,-166.5 372.5,-166.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="373.5,-167.5 373.5,-188.5 504.5,-188.5 504.5,-167.5 373.5,-167.5"/>
-<text text-anchor="start" x="376.5" y="-174.3" font-family="Times,serif" font-size="14.00">PROFILE_SCOPE</text>
-<polygon fill="none" stroke="black" points="372.5,-145.5 372.5,-166.5 505.5,-166.5 505.5,-145.5 372.5,-145.5"/>
-<text text-anchor="start" x="405.5" y="-152.3" font-family="Times,serif" font-size="14.00">profile_id</text>
-<polygon fill="none" stroke="black" points="372.5,-124.5 372.5,-145.5 505.5,-145.5 505.5,-124.5 372.5,-124.5"/>
-<text text-anchor="start" x="408.5" y="-131.3" font-family="Times,serif" font-size="14.00">scope_id</text>
+<polygon fill="lightblue" stroke="transparent" points="372.5,-167.5 372.5,-190.5 505.5,-190.5 505.5,-167.5 372.5,-167.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="373.5,-168.5 373.5,-189.5 504.5,-189.5 504.5,-168.5 373.5,-168.5"/>
+<text text-anchor="start" x="376.5" y="-175.3" font-family="Times,serif" font-size="14.00">PROFILE_SCOPE</text>
+<polygon fill="none" stroke="black" points="372.5,-146.5 372.5,-167.5 505.5,-167.5 505.5,-146.5 372.5,-146.5"/>
+<text text-anchor="start" x="405.5" y="-153.3" font-family="Times,serif" font-size="14.00">profile_id</text>
+<polygon fill="none" stroke="black" points="372.5,-125.5 372.5,-146.5 505.5,-146.5 505.5,-125.5 372.5,-125.5"/>
+<text text-anchor="start" x="408.5" y="-132.3" font-family="Times,serif" font-size="14.00">scope_id</text>
 </g>
 <!-- profile&#45;&gt;profile_scope -->
 <g id="edge5" class="edge">
 <title>profile:pk_profile_id&#45;&gt;profile_scope:fk_profile_id</title>
-<path fill="none" stroke="black" d="M313.5,-345.5C398.06,-345.5 293.42,-171.48 361.72,-156.52"/>
-<polygon fill="black" stroke="black" points="362.05,-156.49 372.45,-159.98 367.02,-155.99 372,-155.5 372,-155.5 372,-155.5 367.02,-155.99 371.55,-151.02 362.05,-156.49 362.05,-156.49"/>
+<path fill="none" stroke="black" d="M313.5,-346.5C398.06,-346.5 293.42,-172.48 361.72,-157.52"/>
+<polygon fill="black" stroke="black" points="362.05,-157.49 372.45,-160.98 367.02,-156.99 372,-156.5 372,-156.5 372,-156.5 367.02,-156.99 371.55,-152.02 362.05,-157.49 362.05,-157.49"/>
 </g>
 <!-- scope -->
 <g id="node4" class="node">
 <title>scope</title>
-<polygon fill="lightblue" stroke="transparent" points="195.5,-125.5 195.5,-148.5 336.5,-148.5 336.5,-125.5 195.5,-125.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="196.5,-126.5 196.5,-147.5 335.5,-147.5 335.5,-126.5 196.5,-126.5"/>
-<text text-anchor="start" x="240" y="-133.3" font-family="Times,serif" font-size="14.00">SCOPE</text>
-<polygon fill="none" stroke="black" points="195.5,-104.5 195.5,-125.5 336.5,-125.5 336.5,-104.5 195.5,-104.5"/>
-<text text-anchor="start" x="235.5" y="-111.3" font-family="Times,serif" font-size="14.00">scope_id</text>
-<polygon fill="none" stroke="black" points="195.5,-83.5 195.5,-104.5 336.5,-104.5 336.5,-83.5 195.5,-83.5"/>
-<text text-anchor="start" x="245.5" y="-90.3" font-family="Times,serif" font-size="14.00">scope</text>
-<polygon fill="none" stroke="black" points="195.5,-62.5 195.5,-83.5 336.5,-83.5 336.5,-62.5 195.5,-62.5"/>
-<text text-anchor="start" x="225.5" y="-69.3" font-family="Times,serif" font-size="14.00">description</text>
-<polygon fill="none" stroke="black" points="195.5,-41.5 195.5,-62.5 336.5,-62.5 336.5,-41.5 195.5,-41.5"/>
-<text text-anchor="start" x="226.5" y="-48.3" font-family="Times,serif" font-size="14.00">application</text>
-<polygon fill="none" stroke="black" points="195.5,-20.5 195.5,-41.5 336.5,-41.5 336.5,-20.5 195.5,-20.5"/>
-<text text-anchor="start" x="217.5" y="-27.3" font-family="Times,serif" font-size="14.00">is_permanent</text>
-<polygon fill="none" stroke="black" points="195.5,0.5 195.5,-20.5 336.5,-20.5 336.5,0.5 195.5,0.5"/>
-<text text-anchor="start" x="198.5" y="-6.3" font-family="Times,serif" font-size="14.00">is_manually_added</text>
+<polygon fill="lightblue" stroke="transparent" points="195.5,-126.5 195.5,-149.5 336.5,-149.5 336.5,-126.5 195.5,-126.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="196.5,-127.5 196.5,-148.5 335.5,-148.5 335.5,-127.5 196.5,-127.5"/>
+<text text-anchor="start" x="240" y="-134.3" font-family="Times,serif" font-size="14.00">SCOPE</text>
+<polygon fill="none" stroke="black" points="195.5,-105.5 195.5,-126.5 336.5,-126.5 336.5,-105.5 195.5,-105.5"/>
+<text text-anchor="start" x="235.5" y="-112.3" font-family="Times,serif" font-size="14.00">scope_id</text>
+<polygon fill="none" stroke="black" points="195.5,-84.5 195.5,-105.5 336.5,-105.5 336.5,-84.5 195.5,-84.5"/>
+<text text-anchor="start" x="245.5" y="-91.3" font-family="Times,serif" font-size="14.00">scope</text>
+<polygon fill="none" stroke="black" points="195.5,-63.5 195.5,-84.5 336.5,-84.5 336.5,-63.5 195.5,-63.5"/>
+<text text-anchor="start" x="225.5" y="-70.3" font-family="Times,serif" font-size="14.00">description</text>
+<polygon fill="none" stroke="black" points="195.5,-42.5 195.5,-63.5 336.5,-63.5 336.5,-42.5 195.5,-42.5"/>
+<text text-anchor="start" x="226.5" y="-49.3" font-family="Times,serif" font-size="14.00">application</text>
+<polygon fill="none" stroke="black" points="195.5,-21.5 195.5,-42.5 336.5,-42.5 336.5,-21.5 195.5,-21.5"/>
+<text text-anchor="start" x="217.5" y="-28.3" font-family="Times,serif" font-size="14.00">is_permanent</text>
+<polygon fill="none" stroke="black" points="195.5,-0.5 195.5,-21.5 336.5,-21.5 336.5,-0.5 195.5,-0.5"/>
+<text text-anchor="start" x="198.5" y="-7.3" font-family="Times,serif" font-size="14.00">is_manually_added</text>
 </g>
 <!-- scope&#45;&gt;token_scope -->
 <g id="edge3" class="edge">
 <title>scope:pk_scope_id&#45;&gt;token_scope:fk_scope_id</title>
-<path fill="none" stroke="black" d="M336,-115.5C411.11,-115.5 446.59,-68.28 505,-115.5 561.48,-161.16 474,-271.95 531.03,-282.66"/>
-<polygon fill="black" stroke="black" points="531.03,-282.66 540.62,-287.98 536.02,-283.08 541,-283.5 541,-283.5 541,-283.5 536.02,-283.08 541.38,-279.02 531.03,-282.66 531.03,-282.66"/>
+<path fill="none" stroke="black" d="M336,-116.5C411.11,-116.5 446.59,-69.28 505,-116.5 561.48,-162.16 474,-272.95 531.03,-283.66"/>
+<polygon fill="black" stroke="black" points="531.03,-283.66 540.62,-288.98 536.02,-284.08 541,-284.5 541,-284.5 541,-284.5 536.02,-284.08 541.38,-280.02 531.03,-283.66 531.03,-283.66"/>
 </g>
 <!-- scope&#45;&gt;profile_scope -->
 <g id="edge6" class="edge">
 <title>scope:pk_scope_id&#45;&gt;profile_scope:fk_scope_id</title>
-<path fill="none" stroke="black" d="M336,-115.5C349.99,-115.5 353.05,-126.87 362.09,-132.02"/>
-<polygon fill="black" stroke="black" points="362.3,-132.07 370.91,-138.86 367.15,-133.28 372,-134.5 372,-134.5 372,-134.5 367.15,-133.28 373.09,-130.14 362.3,-132.07 362.3,-132.07"/>
+<path fill="none" stroke="black" d="M336,-116.5C349.99,-116.5 353.05,-127.87 362.09,-133.02"/>
+<polygon fill="black" stroke="black" points="362.3,-133.07 370.91,-139.86 367.15,-134.28 372,-135.5 372,-135.5 372,-135.5 367.15,-134.28 373.09,-131.14 362.3,-133.07 362.3,-133.07"/>
 </g>
 <!-- authentication -->
 <g id="node6" class="node">
 <title>authentication</title>
-<polygon fill="lightblue" stroke="transparent" points="6.5,-333.5 6.5,-356.5 152.5,-356.5 152.5,-333.5 6.5,-333.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="7.5,-334.5 7.5,-355.5 151.5,-355.5 151.5,-334.5 7.5,-334.5"/>
-<text text-anchor="start" x="10.5" y="-341.3" font-family="Times,serif" font-size="14.00">AUTHENTICATION</text>
-<polygon fill="none" stroke="black" points="6.5,-312.5 6.5,-333.5 152.5,-333.5 152.5,-312.5 6.5,-312.5"/>
-<text text-anchor="start" x="36" y="-319.3" font-family="Times,serif" font-size="14.00">identifier_id</text>
-<polygon fill="none" stroke="black" points="6.5,-291.5 6.5,-312.5 152.5,-312.5 152.5,-291.5 6.5,-291.5"/>
-<text text-anchor="start" x="52" y="-298.3" font-family="Times,serif" font-size="14.00">created</text>
-<polygon fill="none" stroke="black" points="6.5,-270.5 6.5,-291.5 152.5,-291.5 152.5,-270.5 6.5,-270.5"/>
-<text text-anchor="start" x="13" y="-277.3" font-family="Times,serif" font-size="14.00">last_authenticated</text>
-<polygon fill="none" stroke="black" points="6.5,-249.5 6.5,-270.5 152.5,-270.5 152.5,-249.5 6.5,-249.5"/>
-<text text-anchor="start" x="46" y="-256.3" font-family="Times,serif" font-size="14.00">identifier</text>
-<polygon fill="none" stroke="black" points="6.5,-228.5 6.5,-249.5 152.5,-249.5 152.5,-228.5 6.5,-228.5"/>
-<text text-anchor="start" x="43" y="-235.3" font-family="Times,serif" font-size="14.00">credential</text>
+<polygon fill="lightblue" stroke="transparent" points="6.5,-334.5 6.5,-357.5 152.5,-357.5 152.5,-334.5 6.5,-334.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="7.5,-335.5 7.5,-356.5 151.5,-356.5 151.5,-335.5 7.5,-335.5"/>
+<text text-anchor="start" x="10.5" y="-342.3" font-family="Times,serif" font-size="14.00">AUTHENTICATION</text>
+<polygon fill="none" stroke="black" points="6.5,-313.5 6.5,-334.5 152.5,-334.5 152.5,-313.5 6.5,-313.5"/>
+<text text-anchor="start" x="36" y="-320.3" font-family="Times,serif" font-size="14.00">identifier_id</text>
+<polygon fill="none" stroke="black" points="6.5,-292.5 6.5,-313.5 152.5,-313.5 152.5,-292.5 6.5,-292.5"/>
+<text text-anchor="start" x="52" y="-299.3" font-family="Times,serif" font-size="14.00">created</text>
+<polygon fill="none" stroke="black" points="6.5,-271.5 6.5,-292.5 152.5,-292.5 152.5,-271.5 6.5,-271.5"/>
+<text text-anchor="start" x="13" y="-278.3" font-family="Times,serif" font-size="14.00">last_authenticated</text>
+<polygon fill="none" stroke="black" points="6.5,-250.5 6.5,-271.5 152.5,-271.5 152.5,-250.5 6.5,-250.5"/>
+<text text-anchor="start" x="46" y="-257.3" font-family="Times,serif" font-size="14.00">identifier</text>
+<polygon fill="none" stroke="black" points="6.5,-229.5 6.5,-250.5 152.5,-250.5 152.5,-229.5 6.5,-229.5"/>
+<text text-anchor="start" x="43" y="-236.3" font-family="Times,serif" font-size="14.00">credential</text>
+<polygon fill="none" stroke="black" points="6.5,-208.5 6.5,-229.5 152.5,-229.5 152.5,-208.5 6.5,-208.5"/>
+<text text-anchor="start" x="52" y="-215.3" font-family="Times,serif" font-size="14.00">otp_key</text>
 </g>
 <!-- authentication&#45;&gt;profile -->
 <g id="edge4" class="edge">
 <title>authentication:pk_identifier_id&#45;&gt;profile:fk_identifier_id</title>
-<path fill="none" stroke="black" d="M153.5,-323.5C178.66,-323.5 187.39,-323.5 208.32,-323.5"/>
-<polygon fill="black" stroke="black" points="208.5,-323.5 218.5,-328 213.5,-323.5 218.5,-323.5 218.5,-323.5 218.5,-323.5 213.5,-323.5 218.5,-319 208.5,-323.5 208.5,-323.5"/>
+<path fill="none" stroke="black" d="M153.5,-324.5C178.66,-324.5 187.39,-324.5 208.32,-324.5"/>
+<polygon fill="black" stroke="black" points="208.5,-324.5 218.5,-329 213.5,-324.5 218.5,-324.5 218.5,-324.5 218.5,-324.5 213.5,-324.5 218.5,-320 208.5,-324.5 208.5,-324.5"/>
 </g>
 <!-- resource -->
 <g id="node8" class="node">
 <title>resource</title>
-<polygon fill="lightblue" stroke="transparent" points="32.5,-458.5 32.5,-481.5 126.5,-481.5 126.5,-458.5 32.5,-458.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="33.5,-459.5 33.5,-480.5 125.5,-480.5 125.5,-459.5 33.5,-459.5"/>
-<text text-anchor="start" x="36.5" y="-466.3" font-family="Times,serif" font-size="14.00">RESOURCE</text>
-<polygon fill="none" stroke="black" points="32.5,-437.5 32.5,-458.5 126.5,-458.5 126.5,-437.5 32.5,-437.5"/>
-<text text-anchor="start" x="38" y="-444.3" font-family="Times,serif" font-size="14.00">resource_id</text>
-<polygon fill="none" stroke="black" points="32.5,-416.5 32.5,-437.5 126.5,-437.5 126.5,-416.5 32.5,-416.5"/>
-<text text-anchor="start" x="39" y="-423.3" font-family="Times,serif" font-size="14.00">description</text>
-<polygon fill="none" stroke="black" points="32.5,-395.5 32.5,-416.5 126.5,-416.5 126.5,-395.5 32.5,-395.5"/>
-<text text-anchor="start" x="52" y="-402.3" font-family="Times,serif" font-size="14.00">created</text>
-<polygon fill="none" stroke="black" points="32.5,-374.5 32.5,-395.5 126.5,-395.5 126.5,-374.5 32.5,-374.5"/>
-<text text-anchor="start" x="57" y="-381.3" font-family="Times,serif" font-size="14.00">secret</text>
+<polygon fill="lightblue" stroke="transparent" points="32.5,-459.5 32.5,-482.5 126.5,-482.5 126.5,-459.5 32.5,-459.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="33.5,-460.5 33.5,-481.5 125.5,-481.5 125.5,-460.5 33.5,-460.5"/>
+<text text-anchor="start" x="36.5" y="-467.3" font-family="Times,serif" font-size="14.00">RESOURCE</text>
+<polygon fill="none" stroke="black" points="32.5,-438.5 32.5,-459.5 126.5,-459.5 126.5,-438.5 32.5,-438.5"/>
+<text text-anchor="start" x="38" y="-445.3" font-family="Times,serif" font-size="14.00">resource_id</text>
+<polygon fill="none" stroke="black" points="32.5,-417.5 32.5,-438.5 126.5,-438.5 126.5,-417.5 32.5,-417.5"/>
+<text text-anchor="start" x="39" y="-424.3" font-family="Times,serif" font-size="14.00">description</text>
+<polygon fill="none" stroke="black" points="32.5,-396.5 32.5,-417.5 126.5,-417.5 126.5,-396.5 32.5,-396.5"/>
+<text text-anchor="start" x="52" y="-403.3" font-family="Times,serif" font-size="14.00">created</text>
+<polygon fill="none" stroke="black" points="32.5,-375.5 32.5,-396.5 126.5,-396.5 126.5,-375.5 32.5,-375.5"/>
+<text text-anchor="start" x="57" y="-382.3" font-family="Times,serif" font-size="14.00">secret</text>
 </g>
 <!-- almanac -->
 <g id="node9" class="node">
 <title>almanac</title>
-<polygon fill="lightblue" stroke="transparent" points="37.5,-541.5 37.5,-564.5 121.5,-564.5 121.5,-541.5 37.5,-541.5"/>
-<polygon fill="none" stroke="black" stroke-width="2" points="38.5,-542.5 38.5,-563.5 120.5,-563.5 120.5,-542.5 38.5,-542.5"/>
-<text text-anchor="start" x="41.5" y="-549.3" font-family="Times,serif" font-size="14.00">ALMANAC</text>
-<polygon fill="none" stroke="black" points="37.5,-520.5 37.5,-541.5 121.5,-541.5 121.5,-520.5 37.5,-520.5"/>
-<text text-anchor="start" x="59.5" y="-527.3" font-family="Times,serif" font-size="14.00">event</text>
-<polygon fill="none" stroke="black" points="37.5,-499.5 37.5,-520.5 121.5,-520.5 121.5,-499.5 37.5,-499.5"/>
-<text text-anchor="start" x="58" y="-506.3" font-family="Times,serif" font-size="14.00">epoch</text>
+<polygon fill="lightblue" stroke="transparent" points="37.5,-542.5 37.5,-565.5 121.5,-565.5 121.5,-542.5 37.5,-542.5"/>
+<polygon fill="none" stroke="black" stroke-width="2" points="38.5,-543.5 38.5,-564.5 120.5,-564.5 120.5,-543.5 38.5,-543.5"/>
+<text text-anchor="start" x="41.5" y="-550.3" font-family="Times,serif" font-size="14.00">ALMANAC</text>
+<polygon fill="none" stroke="black" points="37.5,-521.5 37.5,-542.5 121.5,-542.5 121.5,-521.5 37.5,-521.5"/>
+<text text-anchor="start" x="59.5" y="-528.3" font-family="Times,serif" font-size="14.00">event</text>
+<polygon fill="none" stroke="black" points="37.5,-500.5 37.5,-521.5 121.5,-521.5 121.5,-500.5 37.5,-500.5"/>
+<text text-anchor="start" x="58" y="-507.3" font-family="Times,serif" font-size="14.00">epoch</text>
 </g>
 </g>
 </svg>
index 620e8cbcf9debdf69cf256030944cc3fb3e5c606..2a3836fbe24cdc6c95cecfb89b4b4f5c71bf6ff8 100644 (file)
       "dependencies": {
         "@squeep/amqp-helper": "git+https://git.squeep.com/squeep-amqp-helper#v1.0.0",
         "@squeep/api-dingus": "^2.1.0",
-        "@squeep/authentication-module": "git+https://git.squeep.com/squeep-authentication-module/#v1.3.2",
+        "@squeep/authentication-module": "git+https://git.squeep.com/squeep-authentication-module/#v1.4.0",
         "@squeep/chores": "git+https://git.squeep.com/squeep-chores/#v1.0.1",
-        "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.5.3",
+        "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.6.0",
         "@squeep/indieauth-helper": "^1.4.1",
-        "@squeep/logger-json-console": "^3.0.1",
+        "@squeep/logger-json-console": "^3.0.2",
         "@squeep/mystery-box": "^2.0.2",
         "@squeep/resource-authentication-module": "git+https://git.squeep.com/squeep-resource-authentication-module#v1.0.1",
         "@squeep/roman": "^1.0.1",
       }
     },
     "node_modules/@ampproject/remapping": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
-      "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
       "dev": true,
       "dependencies": {
-        "@jridgewell/gen-mapping": "^0.3.0",
-        "@jridgewell/trace-mapping": "^0.3.9"
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
       },
       "engines": {
         "node": ">=6.0.0"
       }
     },
     "node_modules/@babel/code-frame": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
-      "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
+      "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
       "dev": true,
       "dependencies": {
-        "@babel/highlight": "^7.23.4",
-        "chalk": "^2.4.2"
+        "@babel/highlight": "^7.24.2",
+        "picocolors": "^1.0.0"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/code-frame/node_modules/ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
-      "dependencies": {
-        "color-convert": "^1.9.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/code-frame/node_modules/chalk": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/code-frame/node_modules/color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
-      "dependencies": {
-        "color-name": "1.1.3"
-      }
-    },
-    "node_modules/@babel/code-frame/node_modules/color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-      "dev": true
-    },
-    "node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
-    "node_modules/@babel/code-frame/node_modules/has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/code-frame/node_modules/supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
-      "dependencies": {
-        "has-flag": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/@babel/compat-data": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz",
-      "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz",
+      "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==",
       "dev": true,
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/core": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.6.tgz",
-      "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==",
+      "version": "7.24.3",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz",
+      "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==",
       "dev": true,
       "dependencies": {
         "@ampproject/remapping": "^2.2.0",
-        "@babel/code-frame": "^7.23.5",
-        "@babel/generator": "^7.23.6",
+        "@babel/code-frame": "^7.24.2",
+        "@babel/generator": "^7.24.1",
         "@babel/helper-compilation-targets": "^7.23.6",
         "@babel/helper-module-transforms": "^7.23.3",
-        "@babel/helpers": "^7.23.6",
-        "@babel/parser": "^7.23.6",
-        "@babel/template": "^7.22.15",
-        "@babel/traverse": "^7.23.6",
-        "@babel/types": "^7.23.6",
+        "@babel/helpers": "^7.24.1",
+        "@babel/parser": "^7.24.1",
+        "@babel/template": "^7.24.0",
+        "@babel/traverse": "^7.24.1",
+        "@babel/types": "^7.24.0",
         "convert-source-map": "^2.0.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz",
-      "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz",
+      "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==",
       "dev": true,
       "dependencies": {
-        "@babel/types": "^7.23.6",
-        "@jridgewell/gen-mapping": "^0.3.2",
-        "@jridgewell/trace-mapping": "^0.3.17",
+        "@babel/types": "^7.24.0",
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25",
         "jsesc": "^2.5.1"
       },
       "engines": {
       }
     },
     "node_modules/@babel/helper-module-imports": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
-      "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
+      "version": "7.24.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz",
+      "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==",
       "dev": true,
       "dependencies": {
-        "@babel/types": "^7.22.15"
+        "@babel/types": "^7.24.0"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/helper-string-parser": {
-      "version": "7.23.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
-      "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz",
+      "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==",
       "dev": true,
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/helpers": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz",
-      "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz",
+      "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==",
       "dev": true,
       "dependencies": {
-        "@babel/template": "^7.22.15",
-        "@babel/traverse": "^7.23.6",
-        "@babel/types": "^7.23.6"
+        "@babel/template": "^7.24.0",
+        "@babel/traverse": "^7.24.1",
+        "@babel/types": "^7.24.0"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/highlight": {
-      "version": "7.23.4",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
-      "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
+      "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
       "dev": true,
       "dependencies": {
         "@babel/helper-validator-identifier": "^7.22.20",
         "chalk": "^2.4.2",
-        "js-tokens": "^4.0.0"
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.0.0"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz",
-      "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz",
+      "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==",
       "dev": true,
       "bin": {
         "parser": "bin/babel-parser.js"
       }
     },
     "node_modules/@babel/template": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
-      "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
+      "version": "7.24.0",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
+      "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
       "dev": true,
       "dependencies": {
-        "@babel/code-frame": "^7.22.13",
-        "@babel/parser": "^7.22.15",
-        "@babel/types": "^7.22.15"
+        "@babel/code-frame": "^7.23.5",
+        "@babel/parser": "^7.24.0",
+        "@babel/types": "^7.24.0"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/traverse": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz",
-      "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz",
+      "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==",
       "dev": true,
       "dependencies": {
-        "@babel/code-frame": "^7.23.5",
-        "@babel/generator": "^7.23.6",
+        "@babel/code-frame": "^7.24.1",
+        "@babel/generator": "^7.24.1",
         "@babel/helper-environment-visitor": "^7.22.20",
         "@babel/helper-function-name": "^7.23.0",
         "@babel/helper-hoist-variables": "^7.22.5",
         "@babel/helper-split-export-declaration": "^7.22.6",
-        "@babel/parser": "^7.23.6",
-        "@babel/types": "^7.23.6",
+        "@babel/parser": "^7.24.1",
+        "@babel/types": "^7.24.0",
         "debug": "^4.3.1",
         "globals": "^11.1.0"
       },
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz",
-      "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==",
+      "version": "7.24.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
+      "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
       "dev": true,
       "dependencies": {
         "@babel/helper-string-parser": "^7.23.4",
       }
     },
     "node_modules/@jridgewell/gen-mapping": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
-      "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+      "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
       "dev": true,
       "dependencies": {
-        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/set-array": "^1.2.1",
         "@jridgewell/sourcemap-codec": "^1.4.10",
-        "@jridgewell/trace-mapping": "^0.3.9"
+        "@jridgewell/trace-mapping": "^0.3.24"
       },
       "engines": {
         "node": ">=6.0.0"
       }
     },
     "node_modules/@jridgewell/resolve-uri": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
-      "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
       "dev": true,
       "engines": {
         "node": ">=6.0.0"
       }
     },
     "node_modules/@jridgewell/set-array": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
-      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
       "dev": true,
       "engines": {
         "node": ">=6.0.0"
       "dev": true
     },
     "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.20",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
-      "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
+      "version": "0.3.25",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
       "dev": true,
       "dependencies": {
         "@jridgewell/resolve-uri": "^3.1.0",
       }
     },
     "node_modules/@mapbox/node-pre-gyp": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
-      "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz",
+      "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==",
       "optional": true,
       "dependencies": {
-        "detect-libc": "^2.0.0",
+        "detect-libc": "^1.0.3",
         "https-proxy-agent": "^5.0.0",
         "make-dir": "^3.1.0",
-        "node-fetch": "^2.6.7",
+        "node-fetch": "^2.6.1",
         "nopt": "^5.0.0",
-        "npmlog": "^5.0.1",
+        "npmlog": "^4.1.2",
         "rimraf": "^3.0.2",
-        "semver": "^7.3.5",
-        "tar": "^6.1.11"
+        "semver": "^7.3.4",
+        "tar": "^6.1.0"
       },
       "bin": {
         "node-pre-gyp": "bin/node-pre-gyp"
       }
     },
     "node_modules/@sinonjs/commons": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
-      "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+      "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
       "dev": true,
       "dependencies": {
         "type-detect": "4.0.8"
       }
     },
     "node_modules/@squeep/authentication-module": {
-      "version": "1.3.2",
-      "resolved": "git+https://git.squeep.com/squeep-authentication-module/#8b998e55749e8613c0dece7a156b5edf83fb3608",
+      "version": "1.4.0",
+      "resolved": "git+https://git.squeep.com/squeep-authentication-module/#9c604adfcde56e35767e3eba70890308ec2d3110",
       "license": "ISC",
       "dependencies": {
-        "@squeep/api-dingus": "v2.0.0",
-        "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.4.0",
-        "@squeep/indieauth-helper": "^1.3.0",
-        "@squeep/mystery-box": "^2.0.1"
+        "@squeep/api-dingus": "^2.1.0",
+        "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.6.0",
+        "@squeep/indieauth-helper": "^1.4.1",
+        "@squeep/mystery-box": "^2.0.2",
+        "@squeep/totp": "^1.1.4"
       },
       "engines": {
-        "node": "^14 >=14.18 || >=15.7"
+        "node": "^18"
       },
       "optionalDependencies": {
-        "argon2": "^0.31.0",
+        "argon2": "^0.40.1",
         "node-linux-pam": "^0.2.1"
       }
     },
-    "node_modules/@squeep/authentication-module/node_modules/@squeep/api-dingus": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@squeep/api-dingus/-/api-dingus-2.0.0.tgz",
-      "integrity": "sha512-HKz/yB1KNmEcHB92KIvrNvwMph5fSdJBrxKSgERYfyQkLFl2vSwDV+IlFvi68DYmMBP3lWKzQcTXWBMYlW3c0g==",
-      "dependencies": {
-        "mime-db": "^1.52.0",
-        "uuid": "^9.0.0"
-      },
-      "engines": {
-        "node": ">=14"
-      }
-    },
     "node_modules/@squeep/chores": {
       "version": "1.0.1",
       "resolved": "git+https://git.squeep.com/squeep-chores/#a77e8814cbba0ad751e249850d1f7b144da6446b",
       }
     },
     "node_modules/@squeep/html-template-helper": {
-      "version": "1.4.0",
-      "resolved": "git+https://git.squeep.com/squeep-html-template-helper#100046316a87631fb8814f80b35647709e6c7319",
+      "version": "1.6.0",
+      "resolved": "git+https://git.squeep.com/squeep-html-template-helper#2d0ba72a2ea35f45c1ab1ac81fce3d0cbe7db419",
       "license": "ISC",
       "dependencies": {
         "@squeep/lazy-property": "^1.1.2"
       }
     },
     "node_modules/@squeep/logger-json-console": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/@squeep/logger-json-console/-/logger-json-console-3.0.1.tgz",
-      "integrity": "sha512-Po4PPtKHoYHuDyx/PMdHdIByCeArVh2McGCw8IxTaFBf+PCdAosZmDNpreKb2Xw7OVpbkX1u3iOmrVgQWv6aBg==",
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@squeep/logger-json-console/-/logger-json-console-3.0.2.tgz",
+      "integrity": "sha512-Qz2QMwhyyRB5sZFB/S6eUt7TnmKCPB6oUqa8SW2gGGOLTcvf+0nbFgqqzlFwtUEwF3KwCmMnOt7lwq5PLrm24Q==",
       "engines": {
         "node": ">=17"
       }
         "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
       }
     },
+    "node_modules/@squeep/totp": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/@squeep/totp/-/totp-1.1.4.tgz",
+      "integrity": "sha512-cMoicNB5xIDMdcOtTfkzWZ0eQCepatTsFoWXtQ8Ja4FfvAA3ZWwIMfKV4K7zbx1MjYGF/Ufikxa6CaPS6yd5mw==",
+      "dependencies": {
+        "base32.js": "^0.1.0",
+        "qrcode-svg": "^1.1.0"
+      },
+      "engines": {
+        "node": ">14"
+      }
+    },
     "node_modules/@squeep/web-linking": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/@squeep/web-linking/-/web-linking-1.0.8.tgz",
       "optional": true
     },
     "node_modules/acorn": {
-      "version": "8.11.2",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
-      "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
+      "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"
       }
     },
     "node_modules/aproba": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
-      "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
       "optional": true
     },
     "node_modules/archy": {
       "dev": true
     },
     "node_modules/are-we-there-yet": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
-      "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
+      "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
       "optional": true,
       "dependencies": {
         "delegates": "^1.0.0",
-        "readable-stream": "^3.6.0"
-      },
-      "engines": {
-        "node": ">=10"
+        "readable-stream": "^2.0.6"
       }
     },
+    "node_modules/are-we-there-yet/node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+      "optional": true
+    },
     "node_modules/are-we-there-yet/node_modules/readable-stream": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
-      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
       "optional": true,
       "dependencies": {
-        "inherits": "^2.0.3",
-        "string_decoder": "^1.1.1",
-        "util-deprecate": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
       }
     },
-    "node_modules/are-we-there-yet/node_modules/safe-buffer": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "optional": true
-    },
     "node_modules/are-we-there-yet/node_modules/string_decoder": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
-      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
       "optional": true,
       "dependencies": {
-        "safe-buffer": "~5.2.0"
+        "safe-buffer": "~5.1.0"
       }
     },
     "node_modules/argon2": {
-      "version": "0.31.2",
-      "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.31.2.tgz",
-      "integrity": "sha512-QSnJ8By5Mth60IEte45w9Y7v6bWcQw3YhRtJKKN8oNCxnTLDiv/AXXkDPf2srTMfxFVn3QJdVv2nhXESsUa+Yg==",
+      "version": "0.40.1",
+      "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.1.tgz",
+      "integrity": "sha512-DjtHDwd7pm12qeWyfihHoM8Bn5vGcgH6sKwgPqwNYroRmxlrzadHEvMyuvQxN/V8YSyRRKD5x6ito09q1e9OyA==",
       "hasInstallScript": true,
       "optional": true,
       "dependencies": {
-        "@mapbox/node-pre-gyp": "^1.0.11",
         "@phc/format": "^1.0.0",
-        "node-addon-api": "^7.0.0"
+        "node-addon-api": "^7.1.0",
+        "node-gyp-build": "^4.8.0"
       },
       "engines": {
-        "node": ">=14.0.0"
+        "node": ">=16.17.0"
       }
     },
     "node_modules/argparse": {
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "devOptional": true
     },
+    "node_modules/base32.js": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz",
+      "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
     "node_modules/base64-js": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
       }
     },
     "node_modules/binary-extensions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
-      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
       "dev": true,
       "engines": {
         "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/bindings": {
       "dev": true
     },
     "node_modules/browserslist": {
-      "version": "4.22.2",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz",
-      "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
+      "version": "4.23.0",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+      "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
       "dev": true,
       "funding": [
         {
         }
       ],
       "dependencies": {
-        "caniuse-lite": "^1.0.30001565",
-        "electron-to-chromium": "^1.4.601",
+        "caniuse-lite": "^1.0.30001587",
+        "electron-to-chromium": "^1.4.668",
         "node-releases": "^2.0.14",
         "update-browserslist-db": "^1.0.13"
       },
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001571",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001571.tgz",
-      "integrity": "sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==",
+      "version": "1.0.30001600",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
+      "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
       "dev": true,
       "funding": [
         {
         "wrap-ansi": "^7.0.0"
       }
     },
+    "node_modules/cliui/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cliui/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/code-point-at": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
       "devOptional": true
     },
-    "node_modules/color-support": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
-      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
-      "optional": true,
-      "bin": {
-        "color-support": "bin.js"
-      }
-    },
     "node_modules/commander": {
       "version": "2.17.1",
       "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
       "optional": true
     },
     "node_modules/detect-libc": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
-      "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+      "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+      "optional": true,
+      "bin": {
+        "detect-libc": "bin/detect-libc.js"
+      },
       "engines": {
-        "node": ">=8"
+        "node": ">=0.10"
       }
     },
     "node_modules/diff": {
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.616",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz",
-      "integrity": "sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==",
+      "version": "1.4.715",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz",
+      "integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==",
       "dev": true
     },
     "node_modules/emoji-regex": {
       "dev": true
     },
     "node_modules/escalade": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
-      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+      "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
       "dev": true,
       "engines": {
         "node": ">=6"
       }
     },
     "node_modules/eslint-compat-utils": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz",
-      "integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==",
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz",
+      "integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==",
       "dev": true,
+      "dependencies": {
+        "semver": "^7.5.4"
+      },
       "engines": {
         "node": ">=12"
       },
       }
     },
     "node_modules/eslint-plugin-es-x": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz",
-      "integrity": "sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.6.0.tgz",
+      "integrity": "sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.1.2",
         "@eslint-community/regexpp": "^4.6.0",
-        "eslint-compat-utils": "^0.1.2"
+        "eslint-compat-utils": "^0.5.0"
       },
       "engines": {
         "node": "^14.18.0 || >=16.0.0"
       "dev": true
     },
     "node_modules/fastq": {
-      "version": "1.16.0",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz",
-      "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==",
+      "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/flatted": {
-      "version": "3.2.9",
-      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
-      "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+      "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
       "dev": true
     },
     "node_modules/foreground-child": {
       }
     },
     "node_modules/gauge": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
-      "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+      "version": "2.7.4",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+      "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
       "optional": true,
       "dependencies": {
-        "aproba": "^1.0.3 || ^2.0.0",
-        "color-support": "^1.1.2",
+        "aproba": "^1.0.3",
         "console-control-strings": "^1.0.0",
-        "has-unicode": "^2.0.1",
-        "object-assign": "^4.1.1",
+        "has-unicode": "^2.0.0",
+        "object-assign": "^4.1.0",
         "signal-exit": "^3.0.0",
-        "string-width": "^4.2.3",
-        "strip-ansi": "^6.0.1",
-        "wide-align": "^1.1.2"
-      },
-      "engines": {
-        "node": ">=10"
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wide-align": "^1.1.0"
       }
     },
-    "node_modules/gensync": {
-      "version": "1.0.0-beta.2",
-      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+    "node_modules/gauge/node_modules/ansi-regex": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+      "optional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/gauge/node_modules/strip-ansi": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+      "optional": true,
+      "dependencies": {
+        "ansi-regex": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
       "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
       "dev": true,
       "engines": {
       "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
     },
     "node_modules/glob": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
-      "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
-      "devOptional": true,
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+      "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+      "dev": true,
       "dependencies": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
         "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
+        "minimatch": "^5.0.1",
+        "once": "^1.3.0"
       },
       "engines": {
-        "node": "*"
+        "node": ">=12"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
         "node": ">=10.13.0"
       }
     },
+    "node_modules/glob/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/glob/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/globals": {
       "version": "13.24.0",
       "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
       }
     },
     "node_modules/hasown": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
-      "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
       "dev": true,
       "dependencies": {
         "function-bind": "^1.1.2"
       ]
     },
     "node_modules/ignore": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
-      "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
+      "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/is-fullwidth-code-point": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "devOptional": true,
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+      "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
+      "optional": true,
+      "dependencies": {
+        "number-is-nan": "^1.0.0"
+      },
       "engines": {
-        "node": ">=8"
+        "node": ">=0.10.0"
       }
     },
     "node_modules/is-glob": {
       }
     },
     "node_modules/istanbul-reports": {
-      "version": "3.1.6",
-      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz",
-      "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",
+      "version": "3.1.7",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
+      "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
       "dev": true,
       "dependencies": {
         "html-escaper": "^2.0.0",
       }
     },
     "node_modules/just-extend": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz",
-      "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==",
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz",
+      "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==",
       "dev": true
     },
     "node_modules/keyv": {
         "balanced-match": "^1.0.0"
       }
     },
-    "node_modules/mocha/node_modules/glob": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
-      "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
-      "dev": true,
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^5.0.1",
-        "once": "^1.3.0"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/mocha/node_modules/minimatch": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
       "dev": true
     },
     "node_modules/nise": {
-      "version": "5.1.5",
-      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz",
-      "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==",
-      "dev": true,
-      "dependencies": {
-        "@sinonjs/commons": "^2.0.0",
-        "@sinonjs/fake-timers": "^10.0.2",
-        "@sinonjs/text-encoding": "^0.7.1",
-        "just-extend": "^4.0.2",
-        "path-to-regexp": "^1.7.0"
-      }
-    },
-    "node_modules/nise/node_modules/@sinonjs/commons": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
-      "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
-      "dev": true,
-      "dependencies": {
-        "type-detect": "4.0.8"
-      }
-    },
-    "node_modules/nise/node_modules/@sinonjs/fake-timers": {
-      "version": "10.3.0",
-      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
-      "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
-      "dev": true,
-      "dependencies": {
-        "@sinonjs/commons": "^3.0.0"
-      }
-    },
-    "node_modules/nise/node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
-      "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
+      "version": "5.1.9",
+      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz",
+      "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==",
       "dev": true,
       "dependencies": {
-        "type-detect": "4.0.8"
+        "@sinonjs/commons": "^3.0.0",
+        "@sinonjs/fake-timers": "^11.2.2",
+        "@sinonjs/text-encoding": "^0.7.2",
+        "just-extend": "^6.2.0",
+        "path-to-regexp": "^6.2.1"
       }
     },
     "node_modules/no-case": {
       }
     },
     "node_modules/node-abi": {
-      "version": "3.52.0",
-      "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.52.0.tgz",
-      "integrity": "sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ==",
+      "version": "3.56.0",
+      "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.56.0.tgz",
+      "integrity": "sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==",
       "dependencies": {
         "semver": "^7.3.5"
       },
         }
       }
     },
+    "node_modules/node-gyp-build": {
+      "version": "4.8.0",
+      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz",
+      "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==",
+      "optional": true,
+      "bin": {
+        "node-gyp-build": "bin.js",
+        "node-gyp-build-optional": "optional.js",
+        "node-gyp-build-test": "build-test.js"
+      }
+    },
     "node_modules/node-linux-pam": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/node-linux-pam/-/node-linux-pam-0.2.1.tgz",
         "node": ">=8.6.0"
       }
     },
-    "node_modules/node-linux-pam/node_modules/@mapbox/node-pre-gyp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz",
-      "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==",
-      "optional": true,
-      "dependencies": {
-        "detect-libc": "^1.0.3",
-        "https-proxy-agent": "^5.0.0",
-        "make-dir": "^3.1.0",
-        "node-fetch": "^2.6.1",
-        "nopt": "^5.0.0",
-        "npmlog": "^4.1.2",
-        "rimraf": "^3.0.2",
-        "semver": "^7.3.4",
-        "tar": "^6.1.0"
-      },
-      "bin": {
-        "node-pre-gyp": "bin/node-pre-gyp"
-      }
-    },
-    "node_modules/node-linux-pam/node_modules/ansi-regex": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
-      "optional": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/node-linux-pam/node_modules/aproba": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
-      "optional": true
-    },
-    "node_modules/node-linux-pam/node_modules/are-we-there-yet": {
-      "version": "1.1.7",
-      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
-      "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
-      "optional": true,
-      "dependencies": {
-        "delegates": "^1.0.0",
-        "readable-stream": "^2.0.6"
-      }
-    },
     "node_modules/node-linux-pam/node_modules/cliui": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
         "wrap-ansi": "^6.2.0"
       }
     },
-    "node_modules/node-linux-pam/node_modules/cliui/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==",
-      "optional": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/node-linux-pam/node_modules/cliui/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==",
-      "optional": true,
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/node-linux-pam/node_modules/detect-libc": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
-      "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
-      "optional": true,
-      "bin": {
-        "detect-libc": "bin/detect-libc.js"
-      },
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
     "node_modules/node-linux-pam/node_modules/find-up": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
         "node": ">=8"
       }
     },
-    "node_modules/node-linux-pam/node_modules/gauge": {
-      "version": "2.7.4",
-      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-      "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
-      "optional": true,
-      "dependencies": {
-        "aproba": "^1.0.3",
-        "console-control-strings": "^1.0.0",
-        "has-unicode": "^2.0.0",
-        "object-assign": "^4.1.0",
-        "signal-exit": "^3.0.0",
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wide-align": "^1.1.0"
-      }
-    },
-    "node_modules/node-linux-pam/node_modules/gauge/node_modules/string-width": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-      "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
-      "optional": true,
-      "dependencies": {
-        "code-point-at": "^1.0.0",
-        "is-fullwidth-code-point": "^1.0.0",
-        "strip-ansi": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/node-linux-pam/node_modules/is-fullwidth-code-point": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-      "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
       "optional": true,
-      "dependencies": {
-        "number-is-nan": "^1.0.0"
-      },
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">=8"
       }
     },
-    "node_modules/node-linux-pam/node_modules/isarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
-      "optional": true
-    },
     "node_modules/node-linux-pam/node_modules/locate-path": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
       "integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==",
       "optional": true
     },
-    "node_modules/node-linux-pam/node_modules/npmlog": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
-      "optional": true,
-      "dependencies": {
-        "are-we-there-yet": "~1.1.2",
-        "console-control-strings": "~1.1.0",
-        "gauge": "~2.7.3",
-        "set-blocking": "~2.0.0"
-      }
-    },
     "node_modules/node-linux-pam/node_modules/p-limit": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
         "node": ">=8"
       }
     },
-    "node_modules/node-linux-pam/node_modules/readable-stream": {
-      "version": "2.3.8",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
-      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
-      "optional": true,
-      "dependencies": {
-        "core-util-is": "~1.0.0",
-        "inherits": "~2.0.3",
-        "isarray": "~1.0.0",
-        "process-nextick-args": "~2.0.0",
-        "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.1.1",
-        "util-deprecate": "~1.0.1"
-      }
-    },
-    "node_modules/node-linux-pam/node_modules/string_decoder": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-      "optional": true,
-      "dependencies": {
-        "safe-buffer": "~5.1.0"
-      }
-    },
-    "node_modules/node-linux-pam/node_modules/strip-ansi": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-      "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+    "node_modules/node-linux-pam/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
       "optional": true,
       "dependencies": {
-        "ansi-regex": "^2.0.0"
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
       },
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">=8"
       }
     },
     "node_modules/node-linux-pam/node_modules/wrap-ansi": {
         "node": ">=8"
       }
     },
-    "node_modules/node-linux-pam/node_modules/wrap-ansi/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==",
-      "optional": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/node-linux-pam/node_modules/wrap-ansi/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==",
-      "optional": true,
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/node-linux-pam/node_modules/y18n": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
       }
     },
     "node_modules/normalize-url": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz",
-      "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz",
+      "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==",
       "engines": {
         "node": ">=14.16"
       },
       }
     },
     "node_modules/npmlog": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
-      "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
       "optional": true,
       "dependencies": {
-        "are-we-there-yet": "^2.0.0",
-        "console-control-strings": "^1.1.0",
-        "gauge": "^3.0.0",
-        "set-blocking": "^2.0.0"
+        "are-we-there-yet": "~1.1.2",
+        "console-control-strings": "~1.1.0",
+        "gauge": "~2.7.3",
+        "set-blocking": "~2.0.0"
       }
     },
     "node_modules/number-is-nan": {
         "node": ">=8"
       }
     },
+    "node_modules/nyc/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/nyc/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/nyc/node_modules/locate-path": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
         "node": ">=8"
       }
     },
+    "node_modules/nyc/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/nyc/node_modules/wrap-ansi": {
       "version": "6.2.0",
       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
       "dev": true
     },
     "node_modules/path-to-regexp": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
-      "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
-      "dev": true,
-      "dependencies": {
-        "isarray": "0.0.1"
-      }
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz",
+      "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==",
+      "dev": true
     },
     "node_modules/pg": {
       "version": "8.11.3",
       }
     },
     "node_modules/pg-promise": {
-      "version": "11.5.4",
-      "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-11.5.4.tgz",
-      "integrity": "sha512-esYSkDt2h6NQOkfotGAm1Ld5OjoITJLpB88Z1PIlcAU/RQ0XQE2PxW0bLJEOMHPGV5iaRnj1Y7ARznXbgN4FNw==",
+      "version": "11.5.5",
+      "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-11.5.5.tgz",
+      "integrity": "sha512-DpJkDDH7rG0wUwFRRHimdV6DtG/UTK2SBEKC7KGFR6a5Zuqf9eGThR7dqIaHXnEBDZuWxUfWC5zMRqyk4EP7Lw==",
       "dependencies": {
         "assert-options": "0.8.1",
         "pg": "8.11.3",
       "dev": true
     },
     "node_modules/prebuild-install": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
-      "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz",
+      "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==",
       "dependencies": {
         "detect-libc": "^2.0.0",
         "expand-template": "^2.0.3",
         "node": ">=10"
       }
     },
+    "node_modules/prebuild-install/node_modules/detect-libc": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
+      "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/prelude-ls": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
         "node": ">=6"
       }
     },
+    "node_modules/qrcode-svg": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/qrcode-svg/-/qrcode-svg-1.1.0.tgz",
+      "integrity": "sha512-XyQCIXux1zEIA3NPb0AeR8UMYvXZzWEhgdBgBjH9gO7M48H9uoHzviNz8pXw3UzrAcxRRRn9gxHewAVK7bn9qw==",
+      "bin": {
+        "qrcode-svg": "bin/qrcode-svg.js"
+      }
+    },
     "node_modules/querystringify": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/rimraf/node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "devOptional": 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/run-parallel": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
       }
     },
     "node_modules/semver": {
-      "version": "7.5.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
-      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
       "dependencies": {
         "lru-cache": "^6.0.0"
       },
       }
     },
     "node_modules/sinon/node_modules/diff": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
-      "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==",
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
+      "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
       "dev": true,
       "engines": {
         "node": ">=0.3.1"
       "optional": true
     },
     "node_modules/string-width": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "devOptional": true,
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+      "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
+      "optional": true,
       "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
+        "code-point-at": "^1.0.0",
+        "is-fullwidth-code-point": "^1.0.0",
+        "strip-ansi": "^3.0.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/string-width/node_modules/ansi-regex": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+      "optional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/string-width/node_modules/strip-ansi": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+      "optional": true,
+      "dependencies": {
+        "ansi-regex": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
     "node_modules/strip-ansi": {
       }
     },
     "node_modules/tar": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
-      "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
+      "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
       "optional": true,
       "dependencies": {
         "chownr": "^2.0.0",
         "minimatch": "^3.0.4"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=8"
+      }
+    },
+    "node_modules/test-exclude/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/text-table": {
         "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
       }
     },
+    "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/yargs/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/yargs/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/yocto-queue": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
       }
     },
     "@ampproject/remapping": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
-      "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
       "dev": true,
       "requires": {
-        "@jridgewell/gen-mapping": "^0.3.0",
-        "@jridgewell/trace-mapping": "^0.3.9"
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
       }
     },
     "@babel/code-frame": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
-      "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
+      "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
       "dev": true,
       "requires": {
-        "@babel/highlight": "^7.23.4",
-        "chalk": "^2.4.2"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "color-convert": {
-          "version": "1.9.3",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-          "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-          "dev": true,
-          "requires": {
-            "color-name": "1.1.3"
-          }
-        },
-        "color-name": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-          "dev": true
-        },
-        "escape-string-regexp": {
-          "version": "1.0.5",
-          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-          "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "@babel/highlight": "^7.24.2",
+        "picocolors": "^1.0.0"
       }
     },
     "@babel/compat-data": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz",
-      "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz",
+      "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==",
       "dev": true
     },
     "@babel/core": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.6.tgz",
-      "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==",
+      "version": "7.24.3",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz",
+      "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==",
       "dev": true,
       "requires": {
         "@ampproject/remapping": "^2.2.0",
-        "@babel/code-frame": "^7.23.5",
-        "@babel/generator": "^7.23.6",
+        "@babel/code-frame": "^7.24.2",
+        "@babel/generator": "^7.24.1",
         "@babel/helper-compilation-targets": "^7.23.6",
         "@babel/helper-module-transforms": "^7.23.3",
-        "@babel/helpers": "^7.23.6",
-        "@babel/parser": "^7.23.6",
-        "@babel/template": "^7.22.15",
-        "@babel/traverse": "^7.23.6",
-        "@babel/types": "^7.23.6",
+        "@babel/helpers": "^7.24.1",
+        "@babel/parser": "^7.24.1",
+        "@babel/template": "^7.24.0",
+        "@babel/traverse": "^7.24.1",
+        "@babel/types": "^7.24.0",
         "convert-source-map": "^2.0.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
       }
     },
     "@babel/generator": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz",
-      "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz",
+      "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.23.6",
-        "@jridgewell/gen-mapping": "^0.3.2",
-        "@jridgewell/trace-mapping": "^0.3.17",
+        "@babel/types": "^7.24.0",
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25",
         "jsesc": "^2.5.1"
       }
     },
       }
     },
     "@babel/helper-module-imports": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
-      "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
+      "version": "7.24.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz",
+      "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.22.15"
+        "@babel/types": "^7.24.0"
       }
     },
     "@babel/helper-module-transforms": {
       }
     },
     "@babel/helper-string-parser": {
-      "version": "7.23.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
-      "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz",
+      "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==",
       "dev": true
     },
     "@babel/helper-validator-identifier": {
       "dev": true
     },
     "@babel/helpers": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz",
-      "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz",
+      "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==",
       "dev": true,
       "requires": {
-        "@babel/template": "^7.22.15",
-        "@babel/traverse": "^7.23.6",
-        "@babel/types": "^7.23.6"
+        "@babel/template": "^7.24.0",
+        "@babel/traverse": "^7.24.1",
+        "@babel/types": "^7.24.0"
       }
     },
     "@babel/highlight": {
-      "version": "7.23.4",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
-      "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
+      "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
       "dev": true,
       "requires": {
         "@babel/helper-validator-identifier": "^7.22.20",
         "chalk": "^2.4.2",
-        "js-tokens": "^4.0.0"
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.0.0"
       },
       "dependencies": {
         "ansi-styles": {
       }
     },
     "@babel/parser": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz",
-      "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz",
+      "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==",
       "dev": true
     },
     "@babel/template": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
-      "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
+      "version": "7.24.0",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
+      "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.22.13",
-        "@babel/parser": "^7.22.15",
-        "@babel/types": "^7.22.15"
+        "@babel/code-frame": "^7.23.5",
+        "@babel/parser": "^7.24.0",
+        "@babel/types": "^7.24.0"
       }
     },
     "@babel/traverse": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz",
-      "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz",
+      "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.23.5",
-        "@babel/generator": "^7.23.6",
+        "@babel/code-frame": "^7.24.1",
+        "@babel/generator": "^7.24.1",
         "@babel/helper-environment-visitor": "^7.22.20",
         "@babel/helper-function-name": "^7.23.0",
         "@babel/helper-hoist-variables": "^7.22.5",
         "@babel/helper-split-export-declaration": "^7.22.6",
-        "@babel/parser": "^7.23.6",
-        "@babel/types": "^7.23.6",
+        "@babel/parser": "^7.24.1",
+        "@babel/types": "^7.24.0",
         "debug": "^4.3.1",
         "globals": "^11.1.0"
       },
       }
     },
     "@babel/types": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz",
-      "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==",
+      "version": "7.24.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
+      "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
       "dev": true,
       "requires": {
         "@babel/helper-string-parser": "^7.23.4",
       "dev": true
     },
     "@jridgewell/gen-mapping": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
-      "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+      "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
       "dev": true,
       "requires": {
-        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/set-array": "^1.2.1",
         "@jridgewell/sourcemap-codec": "^1.4.10",
-        "@jridgewell/trace-mapping": "^0.3.9"
+        "@jridgewell/trace-mapping": "^0.3.24"
       }
     },
     "@jridgewell/resolve-uri": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
-      "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
       "dev": true
     },
     "@jridgewell/set-array": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
-      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
       "dev": true
     },
     "@jridgewell/sourcemap-codec": {
       "dev": true
     },
     "@jridgewell/trace-mapping": {
-      "version": "0.3.20",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
-      "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
+      "version": "0.3.25",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
       "dev": true,
       "requires": {
         "@jridgewell/resolve-uri": "^3.1.0",
       }
     },
     "@mapbox/node-pre-gyp": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
-      "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz",
+      "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==",
       "optional": true,
       "requires": {
-        "detect-libc": "^2.0.0",
+        "detect-libc": "^1.0.3",
         "https-proxy-agent": "^5.0.0",
         "make-dir": "^3.1.0",
-        "node-fetch": "^2.6.7",
+        "node-fetch": "^2.6.1",
         "nopt": "^5.0.0",
-        "npmlog": "^5.0.1",
+        "npmlog": "^4.1.2",
         "rimraf": "^3.0.2",
-        "semver": "^7.3.5",
-        "tar": "^6.1.11"
+        "semver": "^7.3.4",
+        "tar": "^6.1.0"
       }
     },
     "@nodelib/fs.scandir": {
       "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g=="
     },
     "@sinonjs/commons": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
-      "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+      "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
       "dev": true,
       "requires": {
         "type-detect": "4.0.8"
       }
     },
     "@squeep/authentication-module": {
-      "version": "git+https://git.squeep.com/squeep-authentication-module/#8b998e55749e8613c0dece7a156b5edf83fb3608",
-      "from": "@squeep/authentication-module@git+https://git.squeep.com/squeep-authentication-module/#v1.3.2",
+      "version": "git+https://git.squeep.com/squeep-authentication-module/#9c604adfcde56e35767e3eba70890308ec2d3110",
+      "from": "@squeep/authentication-module@git+https://git.squeep.com/squeep-authentication-module/#v1.4.0",
       "requires": {
-        "@squeep/api-dingus": "v2.0.0",
-        "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.4.0",
-        "@squeep/indieauth-helper": "^1.3.0",
-        "@squeep/mystery-box": "^2.0.1",
-        "argon2": "^0.31.0",
+        "@squeep/api-dingus": "^2.1.0",
+        "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.6.0",
+        "@squeep/indieauth-helper": "^1.4.1",
+        "@squeep/mystery-box": "^2.0.2",
+        "@squeep/totp": "^1.1.4",
+        "argon2": "^0.40.1",
         "node-linux-pam": "^0.2.1"
-      },
-      "dependencies": {
-        "@squeep/api-dingus": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/@squeep/api-dingus/-/api-dingus-2.0.0.tgz",
-          "integrity": "sha512-HKz/yB1KNmEcHB92KIvrNvwMph5fSdJBrxKSgERYfyQkLFl2vSwDV+IlFvi68DYmMBP3lWKzQcTXWBMYlW3c0g==",
-          "requires": {
-            "mime-db": "^1.52.0",
-            "uuid": "^9.0.0"
-          }
-        }
       }
     },
     "@squeep/chores": {
       }
     },
     "@squeep/html-template-helper": {
-      "version": "git+https://git.squeep.com/squeep-html-template-helper#100046316a87631fb8814f80b35647709e6c7319",
-      "from": "@squeep/html-template-helper@git+https://git.squeep.com/squeep-html-template-helper#v1.5.3",
+      "version": "git+https://git.squeep.com/squeep-html-template-helper#2d0ba72a2ea35f45c1ab1ac81fce3d0cbe7db419",
+      "from": "@squeep/html-template-helper@git+https://git.squeep.com/squeep-html-template-helper#v1.6.0",
       "requires": {
         "@squeep/lazy-property": "^1.1.2"
       }
       "integrity": "sha512-i61ECZLWQI2rhkXj9pDzH1Md5ICghL9zvh5QFVo0BTayuSrdS9SWkJ6gV1qWki/Xz6SuE0y0y145NyHlvOuVaw=="
     },
     "@squeep/logger-json-console": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/@squeep/logger-json-console/-/logger-json-console-3.0.1.tgz",
-      "integrity": "sha512-Po4PPtKHoYHuDyx/PMdHdIByCeArVh2McGCw8IxTaFBf+PCdAosZmDNpreKb2Xw7OVpbkX1u3iOmrVgQWv6aBg=="
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@squeep/logger-json-console/-/logger-json-console-3.0.2.tgz",
+      "integrity": "sha512-Qz2QMwhyyRB5sZFB/S6eUt7TnmKCPB6oUqa8SW2gGGOLTcvf+0nbFgqqzlFwtUEwF3KwCmMnOt7lwq5PLrm24Q=="
     },
     "@squeep/mystery-box": {
       "version": "2.0.2",
         }
       }
     },
+    "@squeep/totp": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/@squeep/totp/-/totp-1.1.4.tgz",
+      "integrity": "sha512-cMoicNB5xIDMdcOtTfkzWZ0eQCepatTsFoWXtQ8Ja4FfvAA3ZWwIMfKV4K7zbx1MjYGF/Ufikxa6CaPS6yd5mw==",
+      "requires": {
+        "base32.js": "^0.1.0",
+        "qrcode-svg": "^1.1.0"
+      }
+    },
     "@squeep/web-linking": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/@squeep/web-linking/-/web-linking-1.0.8.tgz",
       "optional": true
     },
     "acorn": {
-      "version": "8.11.2",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
-      "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
+      "version": "8.11.3",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+      "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
       "dev": true
     },
     "acorn-jsx": {
       }
     },
     "aproba": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
-      "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
       "optional": true
     },
     "archy": {
       "dev": true
     },
     "are-we-there-yet": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
-      "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
+      "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
       "optional": true,
       "requires": {
         "delegates": "^1.0.0",
-        "readable-stream": "^3.6.0"
+        "readable-stream": "^2.0.6"
       },
       "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+          "optional": true
+        },
         "readable-stream": {
-          "version": "3.6.2",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
-          "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+          "version": "2.3.8",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+          "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
           "optional": true,
           "requires": {
-            "inherits": "^2.0.3",
-            "string_decoder": "^1.1.1",
-            "util-deprecate": "^1.0.1"
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
           }
         },
-        "safe-buffer": {
-          "version": "5.2.1",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
-          "optional": true
-        },
         "string_decoder": {
-          "version": "1.3.0",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
-          "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
           "optional": true,
           "requires": {
-            "safe-buffer": "~5.2.0"
+            "safe-buffer": "~5.1.0"
           }
         }
       }
     },
     "argon2": {
-      "version": "0.31.2",
-      "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.31.2.tgz",
-      "integrity": "sha512-QSnJ8By5Mth60IEte45w9Y7v6bWcQw3YhRtJKKN8oNCxnTLDiv/AXXkDPf2srTMfxFVn3QJdVv2nhXESsUa+Yg==",
+      "version": "0.40.1",
+      "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.1.tgz",
+      "integrity": "sha512-DjtHDwd7pm12qeWyfihHoM8Bn5vGcgH6sKwgPqwNYroRmxlrzadHEvMyuvQxN/V8YSyRRKD5x6ito09q1e9OyA==",
       "optional": true,
       "requires": {
-        "@mapbox/node-pre-gyp": "^1.0.11",
         "@phc/format": "^1.0.0",
-        "node-addon-api": "^7.0.0"
+        "node-addon-api": "^7.1.0",
+        "node-gyp-build": "^4.8.0"
       }
     },
     "argparse": {
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "devOptional": true
     },
+    "base32.js": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz",
+      "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ=="
+    },
     "base64-js": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
       }
     },
     "binary-extensions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
-      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
       "dev": true
     },
     "bindings": {
       "dev": true
     },
     "browserslist": {
-      "version": "4.22.2",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz",
-      "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
+      "version": "4.23.0",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+      "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
       "dev": true,
       "requires": {
-        "caniuse-lite": "^1.0.30001565",
-        "electron-to-chromium": "^1.4.601",
+        "caniuse-lite": "^1.0.30001587",
+        "electron-to-chromium": "^1.4.668",
         "node-releases": "^2.0.14",
         "update-browserslist-db": "^1.0.13"
       }
       "devOptional": true
     },
     "caniuse-lite": {
-      "version": "1.0.30001571",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001571.tgz",
-      "integrity": "sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==",
+      "version": "1.0.30001600",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
+      "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
       "dev": true
     },
     "chalk": {
         "string-width": "^4.2.0",
         "strip-ansi": "^6.0.0",
         "wrap-ansi": "^7.0.0"
+      },
+      "dependencies": {
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "4.2.3",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.1"
+          }
+        }
       }
     },
     "code-point-at": {
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
       "devOptional": true
     },
-    "color-support": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
-      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
-      "optional": true
-    },
     "commander": {
       "version": "2.17.1",
       "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
       "optional": true
     },
     "detect-libc": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
-      "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+      "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+      "optional": true
     },
     "diff": {
       "version": "5.0.0",
       }
     },
     "electron-to-chromium": {
-      "version": "1.4.616",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz",
-      "integrity": "sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==",
+      "version": "1.4.715",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz",
+      "integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==",
       "dev": true
     },
     "emoji-regex": {
       "dev": true
     },
     "escalade": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
-      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+      "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
       "dev": true
     },
     "escape-string-regexp": {
       }
     },
     "eslint-compat-utils": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz",
-      "integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==",
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz",
+      "integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==",
       "dev": true,
-      "requires": {}
+      "requires": {
+        "semver": "^7.5.4"
+      }
     },
     "eslint-plugin-es": {
       "version": "3.0.1",
       }
     },
     "eslint-plugin-es-x": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz",
-      "integrity": "sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.6.0.tgz",
+      "integrity": "sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==",
       "dev": true,
       "requires": {
         "@eslint-community/eslint-utils": "^4.1.2",
         "@eslint-community/regexpp": "^4.6.0",
-        "eslint-compat-utils": "^0.1.2"
+        "eslint-compat-utils": "^0.5.0"
       }
     },
     "eslint-plugin-n": {
       "dev": true
     },
     "fastq": {
-      "version": "1.16.0",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz",
-      "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==",
+      "version": "1.17.1",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+      "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
       "dev": true,
       "requires": {
         "reusify": "^1.0.4"
       }
     },
     "flatted": {
-      "version": "3.2.9",
-      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
-      "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+      "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
       "dev": true
     },
     "foreground-child": {
       "dev": true
     },
     "gauge": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
-      "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+      "version": "2.7.4",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+      "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
       "optional": true,
       "requires": {
-        "aproba": "^1.0.3 || ^2.0.0",
-        "color-support": "^1.1.2",
+        "aproba": "^1.0.3",
         "console-control-strings": "^1.0.0",
-        "has-unicode": "^2.0.1",
-        "object-assign": "^4.1.1",
+        "has-unicode": "^2.0.0",
+        "object-assign": "^4.1.0",
         "signal-exit": "^3.0.0",
-        "string-width": "^4.2.3",
-        "strip-ansi": "^6.0.1",
-        "wide-align": "^1.1.2"
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wide-align": "^1.1.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+          "optional": true
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+          "optional": true,
+          "requires": {
+            "ansi-regex": "^2.0.0"
+          }
+        }
       }
     },
     "gensync": {
       "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
     },
     "glob": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
-      "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
-      "devOptional": true,
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+      "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+      "dev": true,
       "requires": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
         "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
+        "minimatch": "^5.0.1",
+        "once": "^1.3.0"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+          "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0"
+          }
+        },
+        "minimatch": {
+          "version": "5.1.6",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+          "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^2.0.1"
+          }
+        }
       }
     },
     "glob-parent": {
       }
     },
     "hasown": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
-      "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
       "dev": true,
       "requires": {
         "function-bind": "^1.1.2"
       "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
     },
     "ignore": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
-      "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+      "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
       "dev": true
     },
     "import-fresh": {
       "dev": true
     },
     "is-fullwidth-code-point": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "devOptional": true
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+      "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
+      "optional": true,
+      "requires": {
+        "number-is-nan": "^1.0.0"
+      }
     },
     "is-glob": {
       "version": "4.0.3",
       }
     },
     "istanbul-reports": {
-      "version": "3.1.6",
-      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz",
-      "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",
+      "version": "3.1.7",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
+      "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
       "dev": true,
       "requires": {
         "html-escaper": "^2.0.0",
       "dev": true
     },
     "just-extend": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz",
-      "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==",
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz",
+      "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==",
       "dev": true
     },
     "keyv": {
             "balanced-match": "^1.0.0"
           }
         },
-        "glob": {
-          "version": "8.1.0",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
-          "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^5.0.1",
-            "once": "^1.3.0"
-          }
-        },
         "minimatch": {
           "version": "5.0.1",
           "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
       "dev": true
     },
     "nise": {
-      "version": "5.1.5",
-      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz",
-      "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==",
+      "version": "5.1.9",
+      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz",
+      "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==",
       "dev": true,
       "requires": {
-        "@sinonjs/commons": "^2.0.0",
-        "@sinonjs/fake-timers": "^10.0.2",
-        "@sinonjs/text-encoding": "^0.7.1",
-        "just-extend": "^4.0.2",
-        "path-to-regexp": "^1.7.0"
-      },
-      "dependencies": {
-        "@sinonjs/commons": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
-          "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
-          "dev": true,
-          "requires": {
-            "type-detect": "4.0.8"
-          }
-        },
-        "@sinonjs/fake-timers": {
-          "version": "10.3.0",
-          "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
-          "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
-          "dev": true,
-          "requires": {
-            "@sinonjs/commons": "^3.0.0"
-          },
-          "dependencies": {
-            "@sinonjs/commons": {
-              "version": "3.0.0",
-              "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
-              "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
-              "dev": true,
-              "requires": {
-                "type-detect": "4.0.8"
-              }
-            }
-          }
-        }
+        "@sinonjs/commons": "^3.0.0",
+        "@sinonjs/fake-timers": "^11.2.2",
+        "@sinonjs/text-encoding": "^0.7.2",
+        "just-extend": "^6.2.0",
+        "path-to-regexp": "^6.2.1"
       }
     },
     "no-case": {
       }
     },
     "node-abi": {
-      "version": "3.52.0",
-      "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.52.0.tgz",
-      "integrity": "sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ==",
+      "version": "3.56.0",
+      "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.56.0.tgz",
+      "integrity": "sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==",
       "requires": {
         "semver": "^7.3.5"
       }
         "whatwg-url": "^5.0.0"
       }
     },
+    "node-gyp-build": {
+      "version": "4.8.0",
+      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz",
+      "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==",
+      "optional": true
+    },
     "node-linux-pam": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/node-linux-pam/-/node-linux-pam-0.2.1.tgz",
         "yargs": "15.4.1"
       },
       "dependencies": {
-        "@mapbox/node-pre-gyp": {
-          "version": "1.0.5",
-          "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz",
-          "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==",
-          "optional": true,
-          "requires": {
-            "detect-libc": "^1.0.3",
-            "https-proxy-agent": "^5.0.0",
-            "make-dir": "^3.1.0",
-            "node-fetch": "^2.6.1",
-            "nopt": "^5.0.0",
-            "npmlog": "^4.1.2",
-            "rimraf": "^3.0.2",
-            "semver": "^7.3.4",
-            "tar": "^6.1.0"
-          }
-        },
-        "ansi-regex": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-          "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
-          "optional": true
-        },
-        "aproba": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-          "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
-          "optional": true
-        },
-        "are-we-there-yet": {
-          "version": "1.1.7",
-          "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
-          "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
-          "optional": true,
-          "requires": {
-            "delegates": "^1.0.0",
-            "readable-stream": "^2.0.6"
-          }
-        },
         "cliui": {
           "version": "6.0.0",
           "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
             "string-width": "^4.2.0",
             "strip-ansi": "^6.0.0",
             "wrap-ansi": "^6.2.0"
-          },
-          "dependencies": {
-            "ansi-regex": {
-              "version": "5.0.1",
-              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-              "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-              "optional": true
-            },
-            "strip-ansi": {
-              "version": "6.0.1",
-              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-              "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-              "optional": true,
-              "requires": {
-                "ansi-regex": "^5.0.1"
-              }
-            }
           }
         },
-        "detect-libc": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
-          "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
-          "optional": true
-        },
         "find-up": {
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
             "path-exists": "^4.0.0"
           }
         },
-        "gauge": {
-          "version": "2.7.4",
-          "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-          "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
-          "optional": true,
-          "requires": {
-            "aproba": "^1.0.3",
-            "console-control-strings": "^1.0.0",
-            "has-unicode": "^2.0.0",
-            "object-assign": "^4.1.0",
-            "signal-exit": "^3.0.0",
-            "string-width": "^1.0.1",
-            "strip-ansi": "^3.0.1",
-            "wide-align": "^1.1.0"
-          },
-          "dependencies": {
-            "string-width": {
-              "version": "1.0.2",
-              "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-              "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
-              "optional": true,
-              "requires": {
-                "code-point-at": "^1.0.0",
-                "is-fullwidth-code-point": "^1.0.0",
-                "strip-ansi": "^3.0.0"
-              }
-            }
-          }
-        },
         "is-fullwidth-code-point": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-          "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
-          "optional": true,
-          "requires": {
-            "number-is-nan": "^1.0.0"
-          }
-        },
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
           "optional": true
         },
         "locate-path": {
           "integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==",
           "optional": true
         },
-        "npmlog": {
-          "version": "4.1.2",
-          "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-          "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
-          "optional": true,
-          "requires": {
-            "are-we-there-yet": "~1.1.2",
-            "console-control-strings": "~1.1.0",
-            "gauge": "~2.7.3",
-            "set-blocking": "~2.0.0"
-          }
-        },
         "p-limit": {
           "version": "2.3.0",
           "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
             "p-limit": "^2.2.0"
           }
         },
-        "readable-stream": {
-          "version": "2.3.8",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
-          "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
-          "optional": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "optional": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-          "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+        "string-width": {
+          "version": "4.2.3",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
           "optional": true,
           "requires": {
-            "ansi-regex": "^2.0.0"
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.1"
           }
         },
         "wrap-ansi": {
             "ansi-styles": "^4.0.0",
             "string-width": "^4.1.0",
             "strip-ansi": "^6.0.0"
-          },
-          "dependencies": {
-            "ansi-regex": {
-              "version": "5.0.1",
-              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-              "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-              "optional": true
-            },
-            "strip-ansi": {
-              "version": "6.0.1",
-              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-              "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-              "optional": true,
-              "requires": {
-                "ansi-regex": "^5.0.1"
-              }
-            }
           }
         },
         "y18n": {
       "dev": true
     },
     "normalize-url": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz",
-      "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw=="
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz",
+      "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w=="
     },
     "npmlog": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
-      "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
       "optional": true,
       "requires": {
-        "are-we-there-yet": "^2.0.0",
-        "console-control-strings": "^1.1.0",
-        "gauge": "^3.0.0",
-        "set-blocking": "^2.0.0"
+        "are-we-there-yet": "~1.1.2",
+        "console-control-strings": "~1.1.0",
+        "gauge": "~2.7.3",
+        "set-blocking": "~2.0.0"
       }
     },
     "number-is-nan": {
             "path-exists": "^4.0.0"
           }
         },
+        "glob": {
+          "version": "7.2.3",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+          "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+          "dev": true,
+          "requires": {
+            "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"
+          }
+        },
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+          "dev": true
+        },
         "locate-path": {
           "version": "5.0.0",
           "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
           "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
           "dev": true
         },
+        "string-width": {
+          "version": "4.2.3",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.1"
+          }
+        },
         "wrap-ansi": {
           "version": "6.2.0",
           "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
       "dev": true
     },
     "path-to-regexp": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
-      "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
-      "dev": true,
-      "requires": {
-        "isarray": "0.0.1"
-      }
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz",
+      "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==",
+      "dev": true
     },
     "pg": {
       "version": "8.11.3",
       "requires": {}
     },
     "pg-promise": {
-      "version": "11.5.4",
-      "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-11.5.4.tgz",
-      "integrity": "sha512-esYSkDt2h6NQOkfotGAm1Ld5OjoITJLpB88Z1PIlcAU/RQ0XQE2PxW0bLJEOMHPGV5iaRnj1Y7ARznXbgN4FNw==",
+      "version": "11.5.5",
+      "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-11.5.5.tgz",
+      "integrity": "sha512-DpJkDDH7rG0wUwFRRHimdV6DtG/UTK2SBEKC7KGFR6a5Zuqf9eGThR7dqIaHXnEBDZuWxUfWC5zMRqyk4EP7Lw==",
       "requires": {
         "assert-options": "0.8.1",
         "pg": "8.11.3",
       }
     },
     "prebuild-install": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
-      "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz",
+      "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==",
       "requires": {
         "detect-libc": "^2.0.0",
         "expand-template": "^2.0.3",
         "simple-get": "^4.0.0",
         "tar-fs": "^2.0.0",
         "tunnel-agent": "^0.6.0"
+      },
+      "dependencies": {
+        "detect-libc": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
+          "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="
+        }
       }
     },
     "prelude-ls": {
       "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
       "dev": true
     },
+    "qrcode-svg": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/qrcode-svg/-/qrcode-svg-1.1.0.tgz",
+      "integrity": "sha512-XyQCIXux1zEIA3NPb0AeR8UMYvXZzWEhgdBgBjH9gO7M48H9uoHzviNz8pXw3UzrAcxRRRn9gxHewAVK7bn9qw=="
+    },
     "querystringify": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
       "devOptional": true,
       "requires": {
         "glob": "^7.1.3"
+      },
+      "dependencies": {
+        "glob": {
+          "version": "7.2.3",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+          "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+          "devOptional": true,
+          "requires": {
+            "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"
+          }
+        }
       }
     },
     "run-parallel": {
       }
     },
     "semver": {
-      "version": "7.5.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
-      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
       "requires": {
         "lru-cache": "^6.0.0"
       },
       },
       "dependencies": {
         "diff": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
-          "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==",
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
+          "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
           "dev": true
         }
       }
       "optional": true
     },
     "string-width": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "devOptional": true,
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+      "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
+      "optional": true,
       "requires": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
+        "code-point-at": "^1.0.0",
+        "is-fullwidth-code-point": "^1.0.0",
+        "strip-ansi": "^3.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+          "optional": true
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+          "optional": true,
+          "requires": {
+            "ansi-regex": "^2.0.0"
+          }
+        }
       }
     },
     "strip-ansi": {
       "dev": true
     },
     "tar": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
-      "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
+      "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
       "optional": true,
       "requires": {
         "chownr": "^2.0.0",
         "@istanbuljs/schema": "^0.1.2",
         "glob": "^7.1.4",
         "minimatch": "^3.0.4"
+      },
+      "dependencies": {
+        "glob": {
+          "version": "7.2.3",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+          "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+          "dev": true,
+          "requires": {
+            "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"
+          }
+        }
       }
     },
     "text-table": {
         "ansi-styles": "^4.0.0",
         "string-width": "^4.1.0",
         "strip-ansi": "^6.0.0"
+      },
+      "dependencies": {
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "4.2.3",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.1"
+          }
+        }
       }
     },
     "wrappy": {
         "string-width": "^4.2.0",
         "y18n": "^5.0.5",
         "yargs-parser": "^20.2.2"
+      },
+      "dependencies": {
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "4.2.3",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.1"
+          }
+        }
       }
     },
     "yargs-parser": {
index d2c2de0a9d8814b4f365a82929db3e46af883079..60f09f763372d03ae29e43e2344cb35c2390e3a1 100644 (file)
   "dependencies": {
     "@squeep/amqp-helper": "git+https://git.squeep.com/squeep-amqp-helper#v1.0.0",
     "@squeep/api-dingus": "^2.1.0",
-    "@squeep/authentication-module": "git+https://git.squeep.com/squeep-authentication-module/#v1.3.2",
+    "@squeep/authentication-module": "git+https://git.squeep.com/squeep-authentication-module/#v1.4.0",
     "@squeep/chores": "git+https://git.squeep.com/squeep-chores/#v1.0.1",
-    "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.5.3",
+    "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.6.0",
     "@squeep/indieauth-helper": "^1.4.1",
-    "@squeep/logger-json-console": "^3.0.1",
+    "@squeep/logger-json-console": "^3.0.2",
     "@squeep/mystery-box": "^2.0.2",
     "@squeep/resource-authentication-module": "git+https://git.squeep.com/squeep-resource-authentication-module#v1.0.1",
     "@squeep/roman": "^1.0.1",
index d87d9ffb8f8fa881602961f5a538666c4b11ac23..3f149fe1c012747bb6363f349ae406045c31dba9 100644 (file)
--- a/server.js
+++ b/server.js
@@ -11,7 +11,7 @@ const { fileScope } = require('./src/common');
 const _fileScope = fileScope(__filename);
 const { version } = require('./package.json');
 
-const PORT = process.env.PORT || 3002;
+const PORT = parseInt(process.env.PORT || 3002);
 const ADDR = process.env.LISTEN_ADDR || '127.0.0.1';
 
 (async function main () {
index d5367df933c109f632140517b55790deb0251045..953034dba67d06d8063275e04a50bb01aa5a3ec1 100644 (file)
@@ -290,13 +290,38 @@ class Database {
    * @param {*} dbCtx
    * @param {String} identifier
    * @param {String} credential
+   * @param {String=} otpKey
    * @returns {Promise<void>}
    */
-  async authenticationUpsert(dbCtx, identifier, credential) {
+  async authenticationUpsert(dbCtx, identifier, credential, otpKey) {
     this._notImplemented('authenticationUpsert', arguments);
   }
 
 
+  /**
+   * Update the otpKey for an identifier.
+   * @param {*} dbCtx
+   * @param {String} identifier
+   * @param {String=} otpKey
+   * @returns {Promise<void>}
+   */
+  async authenticationUpdateOTPKey(dbCtx, identifier, otpKey) {
+    this._notImplemented('authenticationUpdateOTPKey', arguments);
+  }
+
+
+  /**
+   * Update the credential for an identifier.
+   * @param {*} dbCtx
+   * @param {String} identifier
+   * @param {String} credential
+   * @returns {Promise<void>}
+   */
+  async authenticationUpdateCredential(dbCtx, identifier, credential) {
+    this._notImplemented('authenticationUpdateCredentials', arguments);
+  }
+
+
   /**
    * Determine if profile url is known to this service.
    * @param {*} dbCtx
index 184e41b220f570f457764b6ae2b8dd1e215dc53d..8995ab189cfe7a6cc9888dd1d9c8388aedc943d1 100644 (file)
@@ -29,7 +29,7 @@ const schemaVersionsSupported = {
   },
   max: {
     major: 1,
-    minor: 1,
+    minor: 2,
     patch: 0,
   },
 };
@@ -288,20 +288,56 @@ class DatabasePostgres extends Database {
   }
 
 
-  async authenticationUpsert(dbCtx, identifier, credential) {
+  async authenticationUpsert(dbCtx, identifier, credential, otpKey) {
     const _scope = _fileScope('authenticationUpsert');
     const scrubbedCredential = '*'.repeat((credential || '').length);
-    this.logger.debug(_scope, 'called', { identifier, scrubbedCredential });
+    const scrubbedOTPKey = '*'.repeat((otpKey || '').length);
+    this.logger.debug(_scope, 'called', { identifier, scrubbedCredential, scrubbedOTPKey });
 
     try {
-      const result = await dbCtx.result(this.statement.authenticationUpsert, { identifier, credential });
+      const result = await dbCtx.result(this.statement.authenticationUpsert, { identifier, credential, otpKey });
       if (result.rowCount != 1) {
         throw new DBErrors.UnexpectedResult('did not upsert authentication');
       }
+    } catch (e) {
+      this.logger.error(_scope, 'failed', { error: e, identifier, scrubbedCredential, scrubbedOTPKey });
+      throw e;
+    }
+  }
+
+
+  async authenticationUpdateOTPKey(dbCtx, identifier, otpKey = null) {
+    const _scope = _fileScope('authenticationUpdateOTPKey');
+    const scrubbedOTPKey = '*'.repeat((otpKey || '').length);
+    this.logger.debug(_scope, 'called', { identifier, scrubbedOTPKey });
+
+    try {
+      const result = await dbCtx.result(this.statement.authenticationUpdateOtpKey, { identifier, otpKey });
+      if (result.rowCount != 1) {
+        throw new DBErrors.UnexpectedResult('did not update otpKey');
+      }
+    } catch (e) {
+      this.logger.error(_scope, 'failed', { error: e, identifier, scrubbedOTPKey });
+      throw e;
+    }
+  }
+
+
+  async authenticationUpdateCredential(dbCtx, identifier, credential) {
+    const _scope = _fileScope('authenticationUpdateCredential');
+    const scrubbedCredential = '*'.repeat((credential || '').length);
+    this.logger.debug(_scope, 'called', { identifier, scrubbedCredential });
+
+    try {
+      const result = await dbCtx.result(this.statement.authenticationUpdateCredential, { identifier, credential });
+      if (result.rowCount != 1) {
+        throw new DBErrors.UnexpectedResult('did not update credential');
+      }
     } catch (e) {
       this.logger.error(_scope, 'failed', { error: e, identifier, scrubbedCredential });
       throw e;
     }
+
   }
 
 
diff --git a/src/db/postgres/sql/authentication-update-credential.sql b/src/db/postgres/sql/authentication-update-credential.sql
new file mode 100644 (file)
index 0000000..3ae68d3
--- /dev/null
@@ -0,0 +1,4 @@
+--
+UPDATE authentication
+       SET credential = $(credential)
+       WHERE identifier = $(identifier)
diff --git a/src/db/postgres/sql/authentication-update-otp-key.sql b/src/db/postgres/sql/authentication-update-otp-key.sql
new file mode 100644 (file)
index 0000000..98ae3b0
--- /dev/null
@@ -0,0 +1,4 @@
+--
+UPDATE authentication
+       SET otp_key = $(otpKey)
+       WHERE identifier = $(identifier)
index e86d4fbc17674ed2ebcc8a5f2f583bd7268a4ed8..f37892fac77b505b42aae0513af4211725a73dd2 100644 (file)
@@ -1,9 +1,10 @@
 --
 INSERT INTO authentication
-       (identifier, credential)
+       (identifier, credential, otp_key)
 VALUES
-       ($(identifier), $(credential))
+       ($(identifier), $(credential), $(otpKey))
 ON CONFLICT (identifier) DO UPDATE
 SET
        identifier = $(identifier),
-       credential = $(credential)
+       credential = $(credential),
+       otp_key = $(otpKey)
diff --git a/src/db/postgres/sql/schema/1.2.0/apply.sql b/src/db/postgres/sql/schema/1.2.0/apply.sql
new file mode 100644 (file)
index 0000000..f926126
--- /dev/null
@@ -0,0 +1,8 @@
+BEGIN;
+
+       ALTER TABLE authentication ADD COLUMN otp_key TEXT;
+
+       -- Update schema version
+       INSERT INTO _meta_schema_version (major, minor, patch) VALUES (1, 2, 0);
+
+COMMIT;
diff --git a/src/db/postgres/sql/schema/1.2.0/er.dot b/src/db/postgres/sql/schema/1.2.0/er.dot
new file mode 100644 (file)
index 0000000..951f235
--- /dev/null
@@ -0,0 +1,121 @@
+digraph indieAutherERD {
+       graph[
+               rankdir=LR,
+               overlap=false,
+               splines=true,
+               label="IndieAuther Entity-Relations\nPostgres\nSchema 1.1.0",
+               labelloc="t",
+               fontsize=26,
+       ];
+       // layout=neato;
+       node[shape=plain];
+       edge[arrowhead=crow];
+
+       redeemed_ticket [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">REDEEMED_TICKET</td></tr>
+               <tr><td port="pk_ticket_id">ticket_id</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="">subject</td></tr>
+               <tr><td port="">resource</td></tr>
+               <tr><td port="">iss</td></tr>
+               <tr><td port="">token</td></tr>
+               <tr><td port="">ticket</td></tr>
+               <tr><td port="">published</td></tr>
+       </table>
+       >]
+
+       token [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">TOKEN</td></tr>
+               <tr><td port="pk_code_id">code_id</td></tr>
+               <tr><td port="fk_profile_id">profile_id</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="">expires</td></tr>
+               <tr><td port="">refresh_expires</td></tr>
+               <tr><td port="">refreshed</td></tr>
+               <tr><td port="">duration</td></tr>
+               <tr><td port="">refresh_duration</td></tr>
+               <tr><td port="">refresh_count</td></tr>
+               <tr><td port="">is_revoked</td></tr>
+               <tr><td port="">is_token</td></tr>
+               <tr><td port="">client_id</td></tr>
+               <tr><td port="">resource</td></tr>
+               <tr><td port="">profile_data</td></tr>
+       </table>
+       >];
+       profile:pk_profile_id -> token:fk_profile_id;
+
+       scope [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">SCOPE</td></tr>
+               <tr><td port="pk_scope_id">scope_id</td></tr>
+               <tr><td port="">scope</td></tr>
+               <tr><td port="">description</td></tr>
+               <tr><td port="">application</td></tr>
+               <tr><td port="">is_permanent</td></tr>
+               <tr><td port="">is_manually_added</td></tr>
+       </table>
+       >];
+
+       token_scope [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">TOKEN_SCOPE</td></tr>
+               <tr><td port="fk_code_id">code_id</td></tr>
+               <tr><td port="fk_scope_id">scope_id</td></tr>
+       </table>
+       >];
+       token:pk_code_id -> token_scope:fk_code_id;
+       scope:pk_scope_id -> token_scope:fk_scope_id;
+
+       profile [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">PROFILE</td></tr>
+               <tr><td port="pk_profile_id">profile_id</td></tr>
+               <tr><td port="fk_identifier_id">identifier_id</td></tr>
+               <tr><td port="">profile</td></tr>
+       </table>
+       >];
+       authentication:pk_identifier_id -> profile:fk_identifier_id;
+
+       profile_scope [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">PROFILE_SCOPE</td></tr>
+               <tr><td port="fk_profile_id">profile_id</td></tr>
+               <tr><td port="fk_scope_id">scope_id</td></tr>
+       </table>
+       >];
+       profile:pk_profile_id -> profile_scope:fk_profile_id;
+       scope:pk_scope_id -> profile_scope:fk_scope_id;
+
+       authentication [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+                       <tr><td border="2" bgcolor="lightblue">AUTHENTICATION</td></tr>
+                       <tr><td port="pk_identifier_id">identifier_id</td></tr>
+                       <tr><td port="">created</td></tr>
+                       <tr><td port="">last_authenticated</td></tr>
+                       <tr><td port="">identifier</td></tr>
+                       <tr><td port="">credential</td></tr>
+                       <tr><td port="">otp_key</td></tr>
+       </table>
+       >];
+
+       resource [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">RESOURCE</td></tr>
+               <tr><td port="pk_resource_id">resource_id</td></tr>
+               <tr><td port="">description</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="">secret</td></tr>
+       </table>
+       >];
+
+       almanac [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">ALMANAC</td></tr>
+               <tr><td port="pk_event">event</td></tr>
+               <tr><td port="">date</td></tr>
+       </table>
+       >];
+
+}
diff --git a/src/db/postgres/sql/schema/1.2.0/revert.sql b/src/db/postgres/sql/schema/1.2.0/revert.sql
new file mode 100644 (file)
index 0000000..c772b19
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN;
+       ALTER TABLE authentication DROP COLUMN otp_key;
+
+       DELETE FROM _meta_schema_version WHERE major = 1 AND minor = 2 AND patch = 0;
+COMMIT;
index 97027be662733e4cac566a7eccb284bd9256aad0..a618b9abde14fdfff4adfdf7a49bb652baedddc8 100644 (file)
@@ -21,7 +21,7 @@ const schemaVersionsSupported = {
   },
   max: {
     major: 1,
-    minor: 1,
+    minor: 2,
     patch: 0,
   },
 };
@@ -342,17 +342,34 @@ class DatabaseSQLite extends Database {
   }
 
 
-  authenticationUpsert(dbCtx, identifier, credential) {
+  authenticationUpsert(dbCtx, identifier, credential, otpKey) {
     const _scope = _fileScope('authenticationUpsert');
     const scrubbedCredential = '*'.repeat((credential || '').length);
-    this.logger.debug(_scope, 'called', { identifier, scrubbedCredential });
+    const scrubbedOTPKey = '*'.repeat((otpKey || '').length);
+    this.logger.debug(_scope, 'called', { identifier, scrubbedCredential, scrubbedOTPKey });
 
-    let result;
     try {
-      result = this.statement.authenticationUpsert.run({ identifier, credential });
+      const result = this.statement.authenticationUpsert.run({ identifier, credential, otpKey });
       if (result.changes != 1) {
         throw new DBErrors.UnexpectedResult('did not upsert authentication');
       }
+    } catch (e) {
+      this.logger.error(_scope, 'failed', { error: e, identifier, scrubbedCredential, scrubbedOTPKey });
+      throw e;
+    }
+  }
+
+
+  authenticationUpdateCredential(dbCtx, identifier, credential) {
+    const _scope = _fileScope('authenticationUpdateCredential');
+    const scrubbedCredential = '*'.repeat((credential || '').length);
+    this.logger.debug(_scope, 'called', { identifier, scrubbedCredential });
+
+    try {
+      const result = this.statement.authenticationUpdateCredential.run({ identifier, credential });
+      if (result.changes != 1) {
+        throw new DBErrors.UnexpectedResult('did not update credential');
+      }
     } catch (e) {
       this.logger.error(_scope, 'failed', { error: e, identifier, scrubbedCredential });
       throw e;
@@ -360,6 +377,23 @@ class DatabaseSQLite extends Database {
   }
 
 
+  authenticationUpdateOTPKey(dbCtx, identifier, otpKey) {
+    const _scope = _fileScope('authenticationUpdateOTPKey');
+    const scrubbedOTPKey = '*'.repeat((otpKey || '').length);
+    this.logger.debug(_scope, 'called', { identifier, scrubbedOTPKey });
+
+    try {
+      const result = this.statement.authenticationUpdateOtpKey.run({ identifier, otpKey });
+      if (result.changes != 1) {
+        throw new DBErrors.UnexpectedResult('did not update otpKey');
+      }
+    } catch (e) {
+      this.logger.error(_scope, 'failed', { error: e, identifier, scrubbedOTPKey });
+      throw e;
+    }
+  }
+
+
   profileIdentifierInsert(dbCtx, profile, identifier) {
     const _scope = _fileScope('profileIdentifierInsert');
     this.logger.debug(_scope, 'called', { profile, identifier });
index 687e54ac270bd7f7f0505ab70079bec22d5d91ed..261d3e4c7f72de5bb9c1c3f5eb009db86fe69f86 100644 (file)
@@ -3,6 +3,7 @@ SELECT
        created,
        last_authentication,
        identifier,
-       credential
+       credential,
+       otp_key
 FROM authentication
 WHERE identifier = :identifier
diff --git a/src/db/sqlite/sql/authentication-update-credential.sql b/src/db/sqlite/sql/authentication-update-credential.sql
new file mode 100644 (file)
index 0000000..a1e436d
--- /dev/null
@@ -0,0 +1,4 @@
+--
+UPDATE authentication
+       SET credential = :credential
+       WHERE identifier = :identifier
diff --git a/src/db/sqlite/sql/authentication-update-otp-key.sql b/src/db/sqlite/sql/authentication-update-otp-key.sql
new file mode 100644 (file)
index 0000000..7fcc669
--- /dev/null
@@ -0,0 +1,4 @@
+--
+UPDATE authentication
+       SET otp_key = :otpKey
+       WHERE identifier = :identifier
index 8a141ee49bbc748d29af0316d7ea586e6c925fa9..b83071926d346834adf7b4749540489760c17c8a 100644 (file)
@@ -1,9 +1,10 @@
 --
 INSERT INTO authentication
-       (identifier, credential)
+       (identifier, credential, otp_key)
 VALUES
-       (:identifier, :credential)
+       (:identifier, :credential, :otpKey)
 ON CONFLICT (identifier) DO UPDATE
 SET
        identifier = :identifier,
-       credential = :credential
+       credential = :credential,
+       otp_key = :otpKey
diff --git a/src/db/sqlite/sql/schema/1.2.0/apply.sql b/src/db/sqlite/sql/schema/1.2.0/apply.sql
new file mode 100644 (file)
index 0000000..794749b
--- /dev/null
@@ -0,0 +1,8 @@
+BEGIN;
+
+       ALTER TABLE authentication ADD COLUMN otp_key CHECK (typeof(otp_key) IN ('text', 'null'));
+
+       -- Update schema version
+       INSERT INTO _meta_schema_version (major, minor, patch) VALUES (1, 2, 0);
+
+COMMIT;
diff --git a/src/db/sqlite/sql/schema/1.2.0/er.dot b/src/db/sqlite/sql/schema/1.2.0/er.dot
new file mode 100644 (file)
index 0000000..7171617
--- /dev/null
@@ -0,0 +1,121 @@
+digraph indieAutherERD {
+       graph[
+               rankdir=LR,
+               overlap=false,
+               splines=true,
+               label="IndieAuther Entity-Relations\SQLite\nSchema 1.1.0",
+               labelloc="t",
+               fontsize=26,
+       ];
+       // layout=neato;
+       node[shape=plain];
+       edge[arrowhead=crow];
+
+       redeemed_ticket [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">REDEEMED_TICKET</td></tr>
+               <tr><td port="pk_ticket_id">ticket_id</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="">subject</td></tr>
+               <tr><td port="">resource</td></tr>
+               <tr><td port="">iss</td></tr>
+               <tr><td port="">token</td></tr>
+               <tr><td port="">ticket</td></tr>
+               <tr><td port="">published</td></tr>
+       </table>
+       >]
+
+       token [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">TOKEN</td></tr>
+               <tr><td port="pk_code_id">code_id</td></tr>
+               <tr><td port="fk_profile_id">profile_id</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="">expires</td></tr>
+               <tr><td port="">refresh_expires</td></tr>
+               <tr><td port="">refreshed</td></tr>
+               <tr><td port="">duration</td></tr>
+               <tr><td port="">refresh_duration</td></tr>
+               <tr><td port="">refresh_count</td></tr>
+               <tr><td port="">is_revoked</td></tr>
+               <tr><td port="">is_token</td></tr>
+               <tr><td port="">client_id</td></tr>
+               <tr><td port="">resource</td></tr>
+               <tr><td port="">profile_data</td></tr>
+       </table>
+       >];
+       profile:pk_profile_id -> token:fk_profile_id;
+
+       scope [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">SCOPE</td></tr>
+               <tr><td port="pk_scope_id">scope_id</td></tr>
+               <tr><td port="">scope</td></tr>
+               <tr><td port="">description</td></tr>
+               <tr><td port="">application</td></tr>
+               <tr><td port="">is_permanent</td></tr>
+               <tr><td port="">is_manually_added</td></tr>
+       </table>
+       >];
+
+       token_scope [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">TOKEN_SCOPE</td></tr>
+               <tr><td port="fk_code_id">code_id</td></tr>
+               <tr><td port="fk_scope_id">scope_id</td></tr>
+       </table>
+       >];
+       token:pk_code_id -> token_scope:fk_code_id;
+       scope:pk_scope_id -> token_scope:fk_scope_id;
+
+       profile [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">PROFILE</td></tr>
+               <tr><td port="pk_profile_id">profile_id</td></tr>
+               <tr><td port="fk_identifier_id">identifier_id</td></tr>
+               <tr><td port="">profile</td></tr>
+       </table>
+       >];
+       authentication:pk_identifier_id -> profile:fk_identifier_id;
+
+       profile_scope [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">PROFILE_SCOPE</td></tr>
+               <tr><td port="fk_profile_id">profile_id</td></tr>
+               <tr><td port="fk_scope_id">scope_id</td></tr>
+       </table>
+       >];
+       profile:pk_profile_id -> profile_scope:fk_profile_id;
+       scope:pk_scope_id -> profile_scope:fk_scope_id;
+
+       authentication [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+                       <tr><td border="2" bgcolor="lightblue">AUTHENTICATION</td></tr>
+                       <tr><td port="pk_identifier_id">identifier_id</td></tr>
+                       <tr><td port="">created</td></tr>
+                       <tr><td port="">last_authenticated</td></tr>
+                       <tr><td port="">identifier</td></tr>
+                       <tr><td port="">credential</td></tr>
+                       <tr><td port="">otp_key</td></tr>
+       </table>
+       >];
+
+       resource [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">RESOURCE</td></tr>
+               <tr><td port="pk_resource_id">resource_id</td></tr>
+               <tr><td port="">description</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="">secret</td></tr>
+       </table>
+       >];
+
+       almanac [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">ALMANAC</td></tr>
+               <tr><td port="pk_event">event</td></tr>
+               <tr><td port="">epoch</td></tr>
+       </table>
+       >];
+
+}
diff --git a/src/db/sqlite/sql/schema/1.2.0/revert.sql b/src/db/sqlite/sql/schema/1.2.0/revert.sql
new file mode 100644 (file)
index 0000000..c772b19
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN;
+       ALTER TABLE authentication DROP COLUMN otp_key;
+
+       DELETE FROM _meta_schema_version WHERE major = 1 AND minor = 2 AND patch = 0;
+COMMIT;
index 450842ed20367e6a52e182ccff6d6717d4de69a6..5494d275d8e4fb8af173412354497c3e80eacbd7 100644 (file)
@@ -9,18 +9,71 @@
 function sanitizePostCredential(data, sanitize = true) {
   let unclean = false;
 
-  const credentialLength = data?.ctx?.parsedBody?.credential?.length;
-  if (credentialLength) {
-    unclean = true;
-  }
-  if (unclean && sanitize) {
-    data.ctx.parsedBody.credential = '*'.repeat(credentialLength);
-  }
+  [
+    'credential',
+    'credential-old',
+    'credential-new',
+    'credential-new-2',
+  ].forEach((k) => {
+    const credentialLength = data?.ctx?.parsedBody?.[k]?.length; // eslint-disable-line security/detect-object-injection
+    const kUnclean = !!credentialLength;
+    unclean |= kUnclean;
+    if (kUnclean && sanitize) {
+      data.ctx.parsedBody[k] = '*'.repeat(credentialLength); // eslint-disable-line security/detect-object-injection
+    }
+  });
 
   return unclean;
 }
 
 
+/**
+ * Scrub sensitive data from context.
+ * @param {Object} data
+ * @param {Boolean} sanitize
+ * @returns {Boolean}
+ */
+function sanitizeContext(data, sanitize = true) {
+  let unclean = false;
+
+  // hide keys
+  [
+    'otpKey',
+    'otpConfirmKey',
+  ].forEach((k) => {
+    const secretLength = data?.ctx?.[k]?.length; // eslint-disable-line security/detect-object-injection
+    const kUnclean = !! secretLength;
+    unclean |= kUnclean;
+    if (kUnclean && sanitize) {
+      data.ctx[k] = '*'.repeat(secretLength); // eslint-disable-line security/detect-object-injection
+    }
+  });
+
+  // shorten mystery boxes
+  [
+    'otpConfirmBox',
+    'otpState',
+  ].forEach((k) => {
+    const mysteryLength = data?.ctx?.[k]?.length; // eslint-disable-line security/detect-object-injection
+    const mUnclean = !! mysteryLength;
+    unclean |= mUnclean;
+    if (mUnclean && sanitize) {
+      data.ctx[k] = `[scrubbed ${mysteryLength} bytes]`; // eslint-disable-line security/detect-object-injection
+    }
+  });
+
+  const cookieLength = data?.ctx?.cookie?.squeepSession?.length;
+  if (cookieLength) {
+    unclean |= true;
+    if (sanitize) {
+      data.ctx.cookie.squeepSession = `[scrubbed ${cookieLength} bytes]`;
+    }
+  }
+
+  return !! unclean;
+}
+
+
 /**
  * Reduce logged data about scopes from profilesScopes.
  * For all referenced scopes, only include profiles list.
@@ -112,5 +165,6 @@ const _sanitizeProfilesScopes = (scopesEntries, profilesEntries) => {
 
 module.exports = {
   sanitizePostCredential,
+  sanitizeContext,
   reduceScopeVerbosity,
-};
\ No newline at end of file
+};
index b26da2f0fa5fa1f6a9f881357ca076196218a5ab..dc131c35c76a5950f67fa37371a2074e8a12b0d7 100644 (file)
@@ -11,7 +11,7 @@ const common = require('./common');
 const Manager = require('./manager');
 const { Authenticator, SessionManager } = require('@squeep/authentication-module');
 const { ResourceAuthenticator } = require('@squeep/resource-authentication-module');
-const { TemplateHelper: { initContext } } = require('@squeep/html-template-helper');
+const { initContext, navLinks } = require('./template/template-helper');
 const Enum = require('./enum');
 const { ResponseError } = require('./errors');
 
@@ -23,6 +23,7 @@ class Service extends Dingus {
       ...options.dingus,
       ignoreTrailingSlash: false,
     });
+    this.options = options;
     this.asyncLocalStorage = asyncLocalStorage;
     this.staticPath = path.normalize(path.join(__dirname, '..', 'static'));
     this.manager = new Manager(logger, db, options);
@@ -83,6 +84,8 @@ class Service extends Dingus {
     this.on(['GET'], '/admin/login', this.handlerGetAdminLogin.bind(this));
     this.on(['POST'], '/admin/login', this.handlerPostAdminLogin.bind(this));
     this.on(['GET'], '/admin/logout', this.handlerGetAdminLogout.bind(this));
+    this.on(['GET'], '/admin/settings', this.handlerGetAdminSettings.bind(this));
+    this.on(['POST'], '/admin/settings', this.handlerPostAdminSettings.bind(this));
 
     // Page for upkeep info et cetera
     this.on(['GET'], '/admin/maintenance', this.handlerGetAdminMaintenance.bind(this));
@@ -134,7 +137,7 @@ class Service extends Dingus {
 
     await this.authenticator.sessionOptionalLocal(req, res, ctx);
 
-    await this.sessionManager.getAdminLogin(res, ctx);
+    await this.sessionManager.getAdminLogin(res, ctx, navLinks);
   }
 
 
@@ -155,7 +158,46 @@ class Service extends Dingus {
 
     await this.ingestBody(req, res, ctx);
 
-    await this.sessionManager.postAdminLogin(res, ctx);
+    await this.sessionManager.postAdminLogin(res, ctx, navLinks);
+  }
+
+
+  /**
+   * @param {http.IncomingMessage} req
+   * @param {http.ServerResponse} res
+   * @param {Object} ctx
+   */
+  async handlerGetAdminSettings(req, res, ctx) {
+    const _scope = _fileScope('handlerGetAdminSettings');
+    this.logger.debug(_scope, 'called', { req, ctx });
+
+    initContext(ctx);
+
+    this.setResponseType(this.responseTypes, req, res, ctx);
+
+    if (await this.authenticator.sessionRequiredLocal(req, res, ctx)) {
+      await this.sessionManager.getAdminSettings(res, ctx, navLinks);
+    }
+  }
+
+
+  /**
+   * @param {http.IncomingMessage} req
+   * @param {http.ServerResponse} res
+   * @param {Object} ctx
+   */
+  async handlerPostAdminSettings(req, res, ctx) {
+    const _scope = _fileScope('handlerPostAdminSettings');
+    this.logger.debug(_scope, 'called', { req, ctx });
+
+    initContext(ctx);
+
+    this.setResponseType(this.responseTypes, req, res, ctx);
+
+    if (await this.authenticator.sessionRequiredLocal(req, res, ctx)) {
+      await this.ingestBody(req, res, ctx);
+      await this.sessionManager.postAdminSettings(res, ctx, navLinks);
+    }
   }
 
 
index a6a83a0629f3219fc4b7f2ea59965966494f3e33..4fee8e74298e9de7fc22713f700060f1f87d2a9d 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 const th = require('./template-helper');
+const { sessionNavLinks } = require('@squeep/authentication-module');
 
 
 function renderProfileLI(profile) {
@@ -196,17 +197,15 @@ ${tokenTable(ctx.tokens)}
  * @returns {String}
  */
 module.exports = (ctx, options) => {
+  const pagePathLevel = 1;
   const htmlOptions = {
-    pageTitle: options.manager.pageTitle,
+    pageIdentifier: 'admin',
+    pageTitle: options.manager.pageTitle + ' - Admin',
     logoUrl: options.manager.logoUrl,
     footerEntries: options.manager.footerEntries,
-    navLinks: [
-      {
-        text: 'Ticket',
-        href: 'ticket',
-      },
-    ],
   };
+  th.navLinks(pagePathLevel, ctx, htmlOptions);
+  sessionNavLinks(1, ctx, htmlOptions);
   const content = [
     mainContent(ctx),
   ];
index 031eb30c18527602aed4c9a30ae1897432997e39..f57a118b371d396d03c62c6e99c0881f1b786ad4 100644 (file)
@@ -1,6 +1,7 @@
 'use strict';
 
 const th = require('./template-helper');
+const { sessionNavLinks } = require('@squeep/authentication-module');
 
 function renderAlmanacRow(entry) {
   const { event, date } = entry;
@@ -63,24 +64,18 @@ ${Object.entries(chores).map((chore) => renderChoreRow(...chore)).join('\n')}
  * @returns {String}
  */
 module.exports = (ctx, options) => {
+  const pagePathLevel = 1;
   const htmlOptions = {
+    pageIdentifier: 'maintenance',
     pageTitle: options.manager.pageTitle + ' - Maintenance',
     logoUrl: options.manager.logoUrl,
     footerEntries: options.manager.footerEntries,
-    navLinks: [
-      {
-        text: 'Admin',
-        href: '.',
-      },
-      {
-        text: 'Ticket',
-        href: './ticket',
-      },
-    ],
   };
+  th.navLinks(pagePathLevel, ctx, htmlOptions);
+  sessionNavLinks(pagePathLevel, ctx, htmlOptions);
   const content = [
     almanacSection(ctx.almanac || []),
     choresSection(ctx.chores || {}),
   ];
-  return th.htmlPage(1, ctx, htmlOptions, content);
+  return th.htmlPage(pagePathLevel, ctx, htmlOptions, content);
 };
\ No newline at end of file
index 492c40d1dfc3d4fba87a0936cff1883359ed8cc1..37bba69b6bc2f9135aa5891fb86c17b1bc5885d8 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 const th = require('./template-helper');
+const { sessionNavLinks } = require('@squeep/authentication-module');
 
 
 function renderProfileOption(profile) {
@@ -79,20 +80,18 @@ ${scopesCheckboxRows}
  * @returns {String}
  */
 module.exports = (ctx, options) => {
+  const pagePathLevel = 1;
   const htmlOptions = {
+    padeIdentifier: 'ticketProffer',
     pageTitle: options.manager.pageTitle + ' - Ticket Proffer',
     logoUrl: options.manager.logoUrl,
     footerEntries: options.manager.footerEntries,
-    navLinks: [
-      {
-        text: 'Admin',
-        href: '../admin/',
-      },
-    ],
     errorContent: ['Unable to send ticket.'],
   };
+  th.navLinks(pagePathLevel, ctx, htmlOptions);
+  sessionNavLinks(pagePathLevel, ctx, htmlOptions);
   const content = [
     mainContent(ctx),
   ];
-  return th.htmlPage(2, ctx, htmlOptions, content);
+  return th.htmlPage(pagePathLevel, ctx, htmlOptions, content);
 };
\ No newline at end of file
index ee2e4f207874775563fa78a6b99404fcf8e24402..16e3c4870bdb300d9ef031d55a628e40d2f2f205 100644 (file)
@@ -1,7 +1,7 @@
 'use strict';
 
 const th = require('./template-helper');
-
+const { sessionNavLinks } = require('@squeep/authentication-module');
 
 /**
  * 
@@ -16,11 +16,15 @@ const th = require('./template-helper');
  * @returns {String}
  */
 module.exports = (ctx, options) => {
+  const pagePathLevel = 0;
   const htmlOptions = {
+    pageIdentifier: 'authorizationError',
     pageTitle: options.manager.pageTitle,
     logoUrl: options.manager.logoUrl,
     footerEntries: options.manager.footerEntries,
     errorContent: ctx.errorContent || ['Unknown Error'],
   };
-  return th.htmlPage(0, ctx, htmlOptions, []);
+  th.navLinks(pagePathLevel, ctx, htmlOptions);
+  sessionNavLinks(pagePathLevel, ctx, htmlOptions);
+  return th.htmlPage(pagePathLevel, ctx, htmlOptions, []);
 };
\ No newline at end of file
index 910431d94d4560039de9a831c4aee3238013e8b9..bb3d70118d1ebfbdef04814ad4328a60913c0e83 100644 (file)
@@ -1,7 +1,7 @@
 'use strict';
 
 const th = require('./template-helper');
-
+const { sessionNavLinks } = require('@squeep/authentication-module');
 
 /**
  * @param {Object} hApp
@@ -110,7 +110,7 @@ function renderScopeCheckboxLI(scope, checked) {
   }
   let profileClass;
   if (scope.profiles?.length) {
-    profileClass = ['profile-scope'].concat(scope.profiles.map((profile) => th.escapeCSS(profile))).join(' ');
+    profileClass = ['profile-scope'].concat(scope.profiles).join(' ');
   } else {
     profileClass = '';
   }
@@ -344,6 +344,7 @@ function mainContent(ctx, options) { // eslint-disable-line no-unused-vars
  * @returns {String}
  */
 module.exports = (ctx, options) => {
+  const pagePathLevel = 0;
   const htmlOptions = {
     pageTitle: `${options.manager.pageTitle} &mdash; Authorization Request`,
     logoUrl: options.manager.logoUrl,
@@ -357,22 +358,23 @@ function queryAll(query, fn) {
 }
 function profileSelected(element) {
   const profileClass = CSS.escape(element.value);
-  console.log('new profile:', element.value, profileClass);
-  queryAll('.profile-scope input', (n) => n.setAttribute('disabled', true));
+  // queryAll('.profile-scope input', (n) => n.setAttribute('disabled', ''));
   queryAll('.profile-scope', (n) => n.classList.add('disabled'));
   const profileQuery = '.profile-scope.' + profileClass;
-  queryAll(profileQuery + ' input', (n) => n.setAttribute('disabled', false));
+  // queryAll(profileQuery + ' input', (n) => n.removeAttribute('disabled'));
   queryAll(profileQuery, (n) => n.classList.remove('disabled'));
 }
 function onLoad() {
-  return; // The escaped class selection does not seem to work, so ignore it all for now.
   const profileSelect = document.getElementById('me');
   profileSelect.onchange = () => profileSelected(profileSelect);
   profileSelected(profileSelect);
 }
+window.onload = onLoad;
 </script>`,
     ],
   };
+  th.navLinks(pagePathLevel, ctx, htmlOptions);
+  sessionNavLinks(pagePathLevel, ctx, htmlOptions);
   const content = mainContent(ctx, options);
-  return th.htmlPage(0, ctx, htmlOptions, content);
+  return th.htmlPage(pagePathLevel, ctx, htmlOptions, content);
 };
index 1c3f4ebec70a547e9151cd08a1d93e9fbda48f2e..2b7abfe076173c5b5872b122530eedf7dabcd7ab 100644 (file)
@@ -1,6 +1,7 @@
 'use strict';
 
 const th = require('./template-helper');
+const { sessionNavLinks } = require('@squeep/authentication-module');
 
 function aboutSection() {
   return `
@@ -39,28 +40,22 @@ ${contactHTML}
  * @returns {String}
  */
 module.exports = (ctx, options) => {
+  const pagePathLevel = 0;
   const contactHTML = options.adminContactHTML;
   const htmlOptions = {
+    pageIdentifier: 'root',
     pageTitle: options.manager.pageTitle,
     logoUrl: options.manager.logoUrl,
     footerEntries: options.manager.footerEntries,
-    navLinks: [
-      {
-        text: 'Admin',
-        href: 'admin/',
-      },
-      {
-        text: 'Ticket',
-        href: 'admin/ticket',
-      },
-    ],
     headElements: [
       `<link rel="indieauth-metadata" href="${options.dingus.selfBaseUrl}${options.route.metadata}">`,
     ],
   };
+  th.navLinks(pagePathLevel, ctx, htmlOptions);
+  sessionNavLinks(pagePathLevel, ctx, htmlOptions);
   const content = [
     aboutSection(),
     contactSection(contactHTML),
   ];
-  return th.htmlPage(1, ctx, htmlOptions, content);
+  return th.htmlPage(pagePathLevel, ctx, htmlOptions, content);
 };
\ No newline at end of file
index a33c8b004a35677925ba3e1c60b0ac8034b212c4..f15d3e69a99e22dbdb0b9e4412ba82af79aa4f65 100644 (file)
@@ -47,7 +47,34 @@ function scopeCompare([aScope, aDetails], [bScope, bDetails]) {
 }
 
 
+/**
+ * Populate common navLinks for page templates.
+ * @param {Number} pagePathLevel
+ * @param {Object} ctx 
+ * @param {Object} options 
+ */
+function navLinks(pagePathLevel, ctx, options) {
+  if (!options.navLinks) {
+    options.navLinks = [];
+  }
+  const rootPath = '../'.repeat(pagePathLevel);
+
+  if (options.pageIdentifier !== 'admin') {
+    options.navLinks.push({
+      text: 'Admin',
+      href: `${rootPath}admin/`,
+    });
+  }
+  if (options.pageIdentifier !== 'ticketProffer') {
+    options.navLinks.push({
+      text: 'Ticket',
+      href: `${rootPath}admin/ticket`,
+    });
+  }
+}
+
 module.exports = Object.assign(Object.create(TemplateHelper), {
   escapeCSS,
   scopeCompare,
+  navLinks,
 });
\ No newline at end of file
index 65e8c14d62237d9caadbf82a23e97ecc255ee48f..3c06b3195b7a7cc9c624dd75db69977b58b01a62 100644 (file)
@@ -96,6 +96,9 @@ main {
        background-color: lavender;
        padding: .16em;
 }
+.otp-key-qr svg {
+       height: 30vh;
+}
 .code {
        font-family: Courier, monospace, serif;
        font-size: .75em;
index d866af6678cf7bafc79b9bc1f8f6a912ca334ad4..10e88dfc06c40c84d7e65d48b0d27109fc6b44e7 100644 (file)
@@ -137,9 +137,10 @@ describe('Database Integration', function () {
       }); // Resources
 
       describe('Users and Profiles and Scopes', function () {
-        let credential;
+        let credential, otpKey;
         beforeEach(function () {
           credential = '$plain$myPassword';
+          otpKey = '1234567890123456789012';
         });
         step('returns nothing when auth does not exist', async function () {
           await db.context(async (dbCtx) => {
@@ -170,11 +171,27 @@ describe('Database Integration', function () {
         step('update auth entry', async function () {
           await db.context(async (dbCtx) => {
             credential = '$plain$myNewPassword';
-            await db.authenticationUpsert(dbCtx, identifier, credential);
+            await db.authenticationUpsert(dbCtx, identifier, credential, otpKey);
+            const authInfo = await db.authenticationGet(dbCtx, identifier);
+            assert.strictEqual(authInfo.credential, credential);
+            assert.strictEqual(authInfo.otpKey, otpKey);
+          });
+        });
+        step('update auth credential', async function () {
+          await db.context(async (dbCtx) => {
+            credential = '$plain$anotherNewPassword';
+            await db.authenticationUpdateCredential(dbCtx, identifier, credential);
             const authInfo = await db.authenticationGet(dbCtx, identifier);
             assert.strictEqual(authInfo.credential, credential);
           });
         });
+        step('update auth otp', async function () {
+          await db.context(async (dbCtx) => {
+            await db.authenticationUpdateOTPKey(dbCtx, identifier, otpKey);
+            const authInfo = await db.authenticationGet(dbCtx, identifier);
+            assert.strictEqual(authInfo.otpKey, otpKey);
+          });
+        });
         step('profile is not valid', async function () {
           await db.context(async (dbCtx) => {
             const isValid = await db.profileIsValid(dbCtx, profile);
index 3920d340f53e2471b4fe5df55a32d25546996153..c00daad11d7de666c3bd5f6ba19553cb5fc5272e 100644 (file)
@@ -349,7 +349,6 @@ describe('DatabasePostgres', function () {
     });
   }); // almanacUpsert
 
-
   describe('authenticationSuccess', function () {
     let identifier;
     beforeEach(function () {
@@ -420,6 +419,60 @@ describe('DatabasePostgres', function () {
     });
   }); // authenticationUpsert
 
+  describe('authenticationUpdateCredential', function () {
+    let identifier, credential;
+    beforeEach(function () {
+      identifier = 'username';
+      credential = '$z$foo';
+    });
+    it('success', async function () {
+      const dbResult = {
+        rowCount: 1,
+        rows: undefined,
+        duration: 22,
+      };
+      sinon.stub(db.db, 'result').resolves(dbResult);
+      await db.authenticationUpdateCredential(dbCtx, identifier, credential);
+    });
+    it('failure', async function () {
+      credential = undefined;
+      const dbResult = {
+        rowCount: 0,
+        rows: undefined,
+        duration: 22,
+      };
+      sinon.stub(db.db, 'result').resolves(dbResult);
+      await assert.rejects(() => db.authenticationUpdateCredential(dbCtx, identifier, credential), DBErrors.UnexpectedResult);
+
+    });
+  }); // authenticationUpdateCredential
+
+  describe('authenticationUpdateOTPKey', function () {
+    let identifier, otpKey;
+    beforeEach(function () {
+      identifier = 'username';
+      otpKey = '1234567890123456789012';
+    });
+    it('success', async function () {
+      const dbResult = {
+        rowCount: 1,
+        rows: undefined,
+        duration: 22,
+      };
+      sinon.stub(db.db, 'result').resolves(dbResult);
+      await db.authenticationUpdateOTPKey(dbCtx, identifier, otpKey);
+    });
+    it('failure', async function () {
+      const dbResult = {
+        rowCount: 0,
+        rows: undefined,
+        duration: 22,
+      };
+      sinon.stub(db.db, 'result').resolves(dbResult);
+      await assert.rejects(() => db.authenticationUpdateOTPKey(dbCtx, identifier, otpKey), DBErrors.UnexpectedResult);
+    });
+  }); // authenticationUpdateOTPKey
+
   describe('profileIdentifierInsert', function () {
     let profile, identifier;
     beforeEach(function () {
index e79e75fa421db1526db49f523de99bd9d18428ce..f2d95461b439915005833267a8890d60f82ff94e 100644 (file)
@@ -309,6 +309,54 @@ describe('DatabaseSQLite', function () {
     });
   }); // authenticationUpsert
 
+  describe('authenticationUpdateCredential', function () {
+    let identifier, credential;
+    beforeEach(function () {
+      identifier = 'username';
+      credential = '$z$foo';
+    });
+    it('success', function() {
+      const dbResult = {
+        changes: 1,
+        lastInsertRowid: undefined,
+      };
+      sinon.stub(db.statement.authenticationUpdateCredential, 'run').returns(dbResult);
+      db.authenticationUpdateCredential(dbCtx, identifier, credential);
+    });
+    it('failure', function () {
+      const dbResult = {
+        changes: 0,
+        lastInsertRowid: undefined,
+      };
+      sinon.stub(db.statement.authenticationUpdateCredential, 'run').returns(dbResult);
+      assert.throws(() => db.authenticationUpdateCredential(dbCtx, identifier, credential), DBErrors.UnexpectedResult);
+    });
+  }); // authenticationUpdateCredential
+
+  describe('authenticationUpdateOTPKey', function () {
+    let identifier, otpKey;
+    beforeEach(function () {
+      identifier = 'username';
+      otpKey = '1234567890123456789012';
+    });
+    it('success', function() {
+      const dbResult = {
+        changes: 1,
+        lastInsertRowid: undefined,
+      };
+      sinon.stub(db.statement.authenticationUpdateOtpKey, 'run').returns(dbResult);
+      db.authenticationUpdateOTPKey(dbCtx, identifier, otpKey);
+    });
+    it('failure', function () {
+      const dbResult = {
+        changes: 0,
+        lastInsertRowid: undefined,
+      };
+      sinon.stub(db.statement.authenticationUpdateOtpKey, 'run').returns(dbResult);
+      assert.throws(() => db.authenticationUpdateOTPKey(dbCtx, identifier, otpKey), DBErrors.UnexpectedResult);
+    });
+  }); // authenticationUpdateOTPKey
+
   describe('profileIdentifierInsert', function () {
     let profile, identifier;
     beforeEach(function () {
index 3441f940d40251caa678b4e20f5d1f2c839c2ddb..cb5914b3584cb100ba15e91054d9a5758005c9e4 100644 (file)
@@ -51,6 +51,32 @@ describe('Logger', function () {
     assert(logger.backend.info.args[0][0].includes('"********"'));
   });
 
+  it('masks noisy cookie header', function () {
+    logger.info('testScope', 'message', {
+      ctx: {
+        cookie: {
+          squeepSession: 'blahblahblahblahblah',
+        },
+      },
+    });
+    assert(logger.backend.info.called);
+    assert(logger.backend.info.args[0][0].includes('[scrubbed 20 bytes]'));
+  });
+
+  it('masks otp values', function () {
+    logger.info('teestScope', 'message', {
+      ctx: {
+        otpKey: '1234567890123456789012',
+        otpConfirmKey: '1234567890123456789012',
+        otpConfirmBox: 'xxxMysteryxxx',
+        otpState: 'xxxMysteryxxx',
+      }
+    });
+    assert(logger.backend.info.called);
+    assert(!logger.backend.info.args[0][0].includes('"1234567890123456789012"'));
+    assert(!logger.backend.info.args[0][0].includes('"xxxMysteryxxx"'));
+  });
+
   it('strips uninteresting scope dross', function () {
     logger.info('testScope', 'message', {
       ctx: {
index ab6646f19ae9ecccb817388cb8bbb71a18396e82..e55502913d3cccd917b94fe17a92aaeaa80c9ef8 100644 (file)
@@ -106,6 +106,32 @@ describe('Service', function () {
     });
   }); // handlerGetAdminLogout
 
+  describe('handlerGetAdminSettings', function () {
+    it('covers authenticated', async function () {
+      service.authenticator.sessionRequiredLocal.resolves(true);
+      await service.handlerGetAdminSettings(req, res, ctx);
+      assert(service.sessionManager.getAdminSettings.called);
+    });
+    it('covers unauthenticated', async function () {
+      service.authenticator.sessionRequiredLocal.resolves(false);
+      await service.handlerGetAdminSettings(req, res, ctx);
+      assert(service.sessionManager.getAdminSettings.notCalled);
+    });
+  }); // handlerGetAdminSettings
+
+  describe('handlerPostAdminSettings', function () {
+    it('covers authenticated', async function () {
+      service.authenticator.sessionRequiredLocal.resolves(true);
+      await service.handlerPostAdminSettings(req, res, ctx);
+      assert(service.sessionManager.postAdminSettings.called);
+    });
+    it('covers unauthenticated', async function () {
+      service.authenticator.sessionRequiredLocal.resolves(false);
+      await service.handlerPostAdminSettings(req, res, ctx);
+      assert(service.sessionManager.postAdminSettings.notCalled);
+    });
+  }); // handlerPostAdminSettings
+
   describe('handlerGetAdmin', function () {
     it('covers authenticated', async function () {
       service.authenticator.sessionRequiredLocal.resolves(true);
index 7903dd88d86672eeb4918f2d3322e01e6bf3f035..a40293eb1354c21d4eb33f3a012c6b3714cb42c7 100644 (file)
@@ -92,4 +92,34 @@ describe('Template Helper', function () {
     });
   }); // scopeCompare
 
+  describe('navLinks', function () {
+    let pagePathLevel, ctx, options;
+    beforeEach(function () {
+      pagePathLevel = 1;
+      ctx = {};
+      options = {
+        navLinks: [],
+      };
+    });
+    it('populates navLinks', function () {
+      th.navLinks(pagePathLevel, ctx, options);
+      assert.strictEqual(options.navLinks.length, 2);
+    });
+    it('creates and populates navLinks', function () {
+      delete options.navLinks;
+      th.navLinks(pagePathLevel, ctx, options);
+      assert.strictEqual(options.navLinks.length, 2);
+    });
+    it('populates navLink when on admin', function () {
+      options.pageIdentifier = 'admin';
+      th.navLinks(pagePathLevel, ctx, options);
+      assert.strictEqual(options.navLinks.length, 1);
+    });
+    it('populates navLink when on ticketProffer', function () {
+      options.pageIdentifier = 'ticketProffer';
+      th.navLinks(pagePathLevel, ctx, options);
+      assert.strictEqual(options.navLinks.length, 1);
+    });
+  }); // navLinks
+
 }); // Template Helper
index d8cb0cd1f00bbc7babee4491727a4a7569e3f787..054cc47d857810bbfeb33834857f1c665eecb1e2 100644 (file)
@@ -12,6 +12,8 @@ class StubDatabase extends Base {
       'authenticationGet',
       'authenticationSuccess',
       'authenticationUpsert',
+      'authenticationUpdateCredential',
+      'authenticationUpdateOTPKey',
       'profileIdentifierInsert',
       'profileIsValid',
       'profileScopeInsert',