@ofidj/generator-fidj 3.7.5 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -1
- package/bin/create-fidj.cjs +3 -1
- package/generators/app/index.js +2 -0
- package/generators/app/templates/typescript/README.md +1 -1
- package/generators/app/templates/typescript/package.json +1 -1
- package/generators/app/templates/typescript/scripts/render-content.mjs +25 -2
- package/generators/app/templates/typescript/server/index.ts +6 -0
- package/generators/app/templates/typescript/src/content.ts +328 -153
- package/generators/app/templates/typescript/src/main.ts +112 -8
- package/generators/app/templates/typescript/src/provider-window.ts +177 -0
- package/generators/app/templates/typescript/src/service-agreement.ts +80 -23
- package/generators/app/templates/typescript/src/style.css +203 -5
- package/generators/app/templates/typescript/src/tokens.css +5 -0
- package/lib/scaffold.cjs +20 -8
- package/package.json +6 -3
|
@@ -578,11 +578,12 @@ body.has-module {
|
|
|
578
578
|
line-height: 1.55;
|
|
579
579
|
color: var(--fidj-on-ink-faint);
|
|
580
580
|
}
|
|
581
|
+
/* No rule of its own: the disclosure above already draws one across the panel,
|
|
582
|
+
* and two lines a few pixels apart read as a mistake rather than as structure. */
|
|
581
583
|
.signin-trust {
|
|
582
584
|
display: flex;
|
|
583
585
|
flex-direction: column;
|
|
584
586
|
gap: 12px;
|
|
585
|
-
border-top: 1px solid var(--fidj-line-soft);
|
|
586
587
|
padding-top: 20px;
|
|
587
588
|
}
|
|
588
589
|
.signin-trust-head {
|
|
@@ -646,7 +647,10 @@ body.has-module {
|
|
|
646
647
|
.signin-form input {
|
|
647
648
|
min-height: 48px;
|
|
648
649
|
}
|
|
649
|
-
|
|
650
|
+
/* The app's own buttons sit one level deeper now that the credential form is
|
|
651
|
+
* inside a disclosure, so the rule follows them in rather than losing them. */
|
|
652
|
+
.signin-form form > button,
|
|
653
|
+
.signin-form form #email-entry > button {
|
|
650
654
|
min-height: 48px;
|
|
651
655
|
width: 100%;
|
|
652
656
|
margin-top: 12px;
|
|
@@ -780,13 +784,63 @@ body.has-module {
|
|
|
780
784
|
.content-account input:not([type="checkbox"]) {
|
|
781
785
|
width: 100%;
|
|
782
786
|
}
|
|
783
|
-
|
|
787
|
+
/* The app's navigation lives in the app's own bar, past its mark and its name
|
|
788
|
+
* and past a rule — the shape Fidj's console uses for the same job. It used to
|
|
789
|
+
* sit in the page under a bar that carried only a name, which read as two
|
|
790
|
+
* headers for one app and put the way out a scroll away from the way in. */
|
|
791
|
+
.topbar .content-nav {
|
|
792
|
+
border: 0;
|
|
793
|
+
border-left: 1px solid var(--fidj-ink-line-soft);
|
|
794
|
+
border-radius: 0;
|
|
795
|
+
margin: 0 auto 0 0;
|
|
796
|
+
padding-left: clamp(12px, 2vw, 20px);
|
|
797
|
+
gap: 2px;
|
|
798
|
+
flex-wrap: nowrap;
|
|
799
|
+
}
|
|
800
|
+
.topbar .content-nav button {
|
|
801
|
+
padding: 8px 12px;
|
|
802
|
+
font-size: 13.5px;
|
|
803
|
+
color: var(--fidj-on-ink-faint);
|
|
804
|
+
border-bottom: 0;
|
|
805
|
+
white-space: nowrap;
|
|
806
|
+
}
|
|
807
|
+
.topbar .content-nav button:hover:not(:disabled) {
|
|
808
|
+
color: var(--fidj-on-ink);
|
|
809
|
+
}
|
|
810
|
+
.topbar .content-nav button.selected {
|
|
811
|
+
color: var(--fidj-on-ink);
|
|
812
|
+
font-weight: 600;
|
|
813
|
+
}
|
|
814
|
+
.topbar .content-nav button:last-child {
|
|
815
|
+
margin-left: 0;
|
|
816
|
+
}
|
|
817
|
+
/* Who you are, said where it can be checked against what follows. */
|
|
818
|
+
.account-identity {
|
|
819
|
+
font-size: 15px;
|
|
820
|
+
}
|
|
821
|
+
/* The two ways out of this screen, last and together. */
|
|
822
|
+
.account-actions {
|
|
784
823
|
display: flex;
|
|
824
|
+
flex-wrap: wrap;
|
|
785
825
|
align-items: center;
|
|
786
826
|
gap: 10px;
|
|
827
|
+
margin-top: 28px;
|
|
828
|
+
padding-top: 22px;
|
|
829
|
+
border-top: 1px solid var(--fidj-line-soft);
|
|
787
830
|
}
|
|
788
|
-
.
|
|
789
|
-
|
|
831
|
+
.account-actions .primary {
|
|
832
|
+
width: auto;
|
|
833
|
+
flex: 1;
|
|
834
|
+
min-width: 200px;
|
|
835
|
+
margin: 0;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/* The account tab carries the address it is about, and that address is long.
|
|
839
|
+
* It gives way before the app's own name does. */
|
|
840
|
+
.topbar .content-nav #account-tab {
|
|
841
|
+
overflow: hidden;
|
|
842
|
+
text-overflow: ellipsis;
|
|
843
|
+
min-width: 0;
|
|
790
844
|
}
|
|
791
845
|
.signin-logo {
|
|
792
846
|
width: 22px;
|
|
@@ -957,3 +1011,147 @@ button.quiet {
|
|
|
957
1011
|
cursor: pointer;
|
|
958
1012
|
width: auto;
|
|
959
1013
|
}
|
|
1014
|
+
|
|
1015
|
+
/* The Fidj door.
|
|
1016
|
+
*
|
|
1017
|
+
* The accent is the Fidj mark, and this is the one control on an app's entry
|
|
1018
|
+
* that belongs to Fidj rather than to the app. It is also the door the app
|
|
1019
|
+
* would rather people took — the one where the password is never typed here —
|
|
1020
|
+
* and those two reasons happen to want the same paint. Nothing else on the
|
|
1021
|
+
* screen wears it, which is what makes it read as the way in rather than as one
|
|
1022
|
+
* option among several. */
|
|
1023
|
+
.primary.fidj-entry {
|
|
1024
|
+
background: var(--fidj-accent);
|
|
1025
|
+
border-color: var(--fidj-accent);
|
|
1026
|
+
color: var(--fidj-on-accent);
|
|
1027
|
+
margin-top: 0;
|
|
1028
|
+
}
|
|
1029
|
+
.primary.fidj-entry:hover:not(:disabled) {
|
|
1030
|
+
background: var(--fidj-accent-strong);
|
|
1031
|
+
border-color: var(--fidj-accent-strong);
|
|
1032
|
+
}
|
|
1033
|
+
/* While the Fidj window is open, the door is no longer the way in — it is the
|
|
1034
|
+
* way back to the window doing the work. So it reads as a state rather than an
|
|
1035
|
+
* invitation, and keeps the pointer, because it is the one thing on the screen
|
|
1036
|
+
* somebody who has lost that window behind this page needs to be able to press. */
|
|
1037
|
+
.primary.fidj-entry.is-waiting,
|
|
1038
|
+
.primary.fidj-entry.is-waiting:hover:not(:disabled) {
|
|
1039
|
+
background: var(--fidj-surface-sunk);
|
|
1040
|
+
border-color: var(--fidj-line);
|
|
1041
|
+
color: var(--fidj-text-faint);
|
|
1042
|
+
cursor: pointer;
|
|
1043
|
+
}
|
|
1044
|
+
.primary.fidj-entry.is-waiting:hover:not(:disabled) {
|
|
1045
|
+
border-color: var(--fidj-line-hover);
|
|
1046
|
+
color: var(--fidj-text-muted);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
/* The app's own form, second. A disclosure rather than a second button of equal
|
|
1050
|
+
* weight: it costs the person who wants it one click, and costs everybody else
|
|
1051
|
+
* nothing — where two buttons side by side charged every arrival a decision.
|
|
1052
|
+
*
|
|
1053
|
+
* It sits with the button it is an alternative to, not a rule and a gap away:
|
|
1054
|
+
* separated, the two read as unrelated parts of the screen rather than as the
|
|
1055
|
+
* two ways in. */
|
|
1056
|
+
.signin-alternate {
|
|
1057
|
+
margin-top: 2px;
|
|
1058
|
+
}
|
|
1059
|
+
/* A seam across the panel rather than a link hanging off its left edge: the
|
|
1060
|
+
* label in the middle with a rule running out to each side, which is what says
|
|
1061
|
+
* "below this, a different way in" instead of "here is one more control". */
|
|
1062
|
+
.signin-toggle {
|
|
1063
|
+
display: flex;
|
|
1064
|
+
align-items: center;
|
|
1065
|
+
gap: 12px;
|
|
1066
|
+
width: 100%;
|
|
1067
|
+
margin: 0;
|
|
1068
|
+
padding: 8px 0;
|
|
1069
|
+
min-height: 0;
|
|
1070
|
+
background: none;
|
|
1071
|
+
border: none;
|
|
1072
|
+
font: inherit;
|
|
1073
|
+
font-size: 13.5px;
|
|
1074
|
+
color: var(--fidj-text-muted);
|
|
1075
|
+
cursor: pointer;
|
|
1076
|
+
}
|
|
1077
|
+
.signin-toggle::before,
|
|
1078
|
+
.signin-toggle::after {
|
|
1079
|
+
content: "";
|
|
1080
|
+
height: 1px;
|
|
1081
|
+
flex: 1;
|
|
1082
|
+
background: var(--fidj-line);
|
|
1083
|
+
}
|
|
1084
|
+
.signin-toggle-label {
|
|
1085
|
+
display: inline-flex;
|
|
1086
|
+
align-items: center;
|
|
1087
|
+
gap: 7px;
|
|
1088
|
+
}
|
|
1089
|
+
.signin-toggle:hover:not(:disabled) {
|
|
1090
|
+
color: var(--fidj-text);
|
|
1091
|
+
}
|
|
1092
|
+
.signin-toggle:hover:not(:disabled)::before,
|
|
1093
|
+
.signin-toggle:hover:not(:disabled)::after {
|
|
1094
|
+
background: var(--fidj-line-hover);
|
|
1095
|
+
}
|
|
1096
|
+
/* The caret points at what pressing it does: down at a form that is not here
|
|
1097
|
+
* yet, and — turned over — up at the one it would put away again. */
|
|
1098
|
+
.signin-toggle .caret {
|
|
1099
|
+
width: 0;
|
|
1100
|
+
height: 0;
|
|
1101
|
+
border-left: 4px solid transparent;
|
|
1102
|
+
border-right: 4px solid transparent;
|
|
1103
|
+
border-top: 5px solid currentColor;
|
|
1104
|
+
transition: transform 220ms ease;
|
|
1105
|
+
}
|
|
1106
|
+
.signin-toggle[aria-expanded="true"] .caret {
|
|
1107
|
+
transform: rotate(180deg);
|
|
1108
|
+
}
|
|
1109
|
+
.signin-alternate #email-entry {
|
|
1110
|
+
margin-top: 6px;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/* Opening the form folds the Fidj door away, because the two are alternatives
|
|
1114
|
+
* and only one of them is being used. Folding rather than vanishing is the part
|
|
1115
|
+
* that says so: the form visibly takes the room the door was holding. */
|
|
1116
|
+
.primary.fidj-entry {
|
|
1117
|
+
overflow: hidden;
|
|
1118
|
+
max-height: 64px;
|
|
1119
|
+
transition:
|
|
1120
|
+
max-height 220ms ease,
|
|
1121
|
+
min-height 220ms ease,
|
|
1122
|
+
opacity 140ms ease,
|
|
1123
|
+
margin 220ms ease,
|
|
1124
|
+
padding 220ms ease,
|
|
1125
|
+
border-width 220ms ease;
|
|
1126
|
+
}
|
|
1127
|
+
.primary.fidj-entry.is-folded {
|
|
1128
|
+
/* `min-height` wins over `max-height`, so the entry's own 48px floor has to
|
|
1129
|
+
* come down too or nothing moves. */
|
|
1130
|
+
min-height: 0;
|
|
1131
|
+
max-height: 0;
|
|
1132
|
+
opacity: 0;
|
|
1133
|
+
margin-top: 0;
|
|
1134
|
+
margin-bottom: 0;
|
|
1135
|
+
padding-top: 0;
|
|
1136
|
+
padding-bottom: 0;
|
|
1137
|
+
border-width: 0;
|
|
1138
|
+
pointer-events: none;
|
|
1139
|
+
}
|
|
1140
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1141
|
+
.primary.fidj-entry,
|
|
1142
|
+
.signin-toggle .caret {
|
|
1143
|
+
transition: none;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/* A window that opened itself over the page somebody was on owes them the way
|
|
1148
|
+
* back before it asks for a password. */
|
|
1149
|
+
.signin-return {
|
|
1150
|
+
margin: 0 0 18px;
|
|
1151
|
+
padding: 10px 12px;
|
|
1152
|
+
background: var(--fidj-surface-sunk);
|
|
1153
|
+
border-left: 2px solid var(--fidj-accent);
|
|
1154
|
+
color: var(--fidj-text-muted);
|
|
1155
|
+
font-size: 13.5px;
|
|
1156
|
+
line-height: 1.5;
|
|
1157
|
+
}
|
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
/* Accents. The red is the Fidj mark; it identifies the account and privacy
|
|
35
35
|
* provider, so it stays sparing — a dot, a rule, a hover. */
|
|
36
36
|
--fidj-accent: #d6342c;
|
|
37
|
+
/* The one place the accent is a surface rather than a mark: the Fidj door on
|
|
38
|
+
* a sign-in entry. It is the control that belongs to Fidj and not to the app
|
|
39
|
+
* around it, so it is the one thing allowed to carry the mark's weight. */
|
|
40
|
+
--fidj-accent-strong: #b52a23;
|
|
41
|
+
--fidj-on-accent: #fbfaf8;
|
|
37
42
|
--fidj-danger: #b8352c;
|
|
38
43
|
--fidj-danger-strong: #8c2a22;
|
|
39
44
|
--fidj-danger-tint: #fdf7f6;
|
package/lib/scaffold.cjs
CHANGED
|
@@ -25,15 +25,27 @@ function scaffold(destination, options) {
|
|
|
25
25
|
const anonymous = options.anonymous ?? true;
|
|
26
26
|
if (![true, false, "true", "false"].includes(anonymous))
|
|
27
27
|
throw new Error("--anonymous must be true or false.");
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
28
|
+
// How this app asks, in the owner's words.
|
|
29
|
+
//
|
|
30
|
+
// `button` delegates every sign-in to Fidj, which is the promise the entry
|
|
31
|
+
// makes — this app never sees a password — so it is what an owner gets
|
|
32
|
+
// without asking. `inline` keeps the app's own form and nothing else, for an
|
|
33
|
+
// owner who has decided that their page, not only Fidj, is to be trusted with
|
|
34
|
+
// the credential. `both` leads with the button and folds the form under it.
|
|
35
|
+
//
|
|
36
|
+
// One input with three values rather than a boolean, because "no button" and
|
|
37
|
+
// "no form" are different apps and a flag named after one cannot say the
|
|
38
|
+
// other. `--credentials true|false` said the same thing about a product that
|
|
39
|
+
// had only two shapes; it still means what it meant, and `--signin` wins when
|
|
40
|
+
// both are given.
|
|
34
41
|
const credentials = options.credentials ?? false;
|
|
35
42
|
if (![true, false, "true", "false"].includes(credentials))
|
|
36
43
|
throw new Error("--credentials must be true or false.");
|
|
44
|
+
const signin =
|
|
45
|
+
options.signin ??
|
|
46
|
+
(credentials === true || credentials === "true" ? "both" : "button");
|
|
47
|
+
if (!["button", "inline", "both"].includes(signin))
|
|
48
|
+
throw new Error("--signin must be button, inline or both.");
|
|
37
49
|
const name = options.name || path.basename(path.resolve(destination));
|
|
38
50
|
if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(name))
|
|
39
51
|
throw new Error(
|
|
@@ -157,7 +169,7 @@ function scaffold(destination, options) {
|
|
|
157
169
|
// The app's own server serves this to its page: the entry cannot redirect to
|
|
158
170
|
// a provider it was never told about.
|
|
159
171
|
const issuerSetting = options.oidcIssuer ? `FIDJ_OIDC_ISSUER=${options.oidcIssuer}\n` : "";
|
|
160
|
-
const settings = `FIDJ_APP_ID=${options.appId}\nFIDJ_API_ENDPOINT=${api.href.replace(/\/$/, "")}\n${issuerSetting}FIDJ_DASHBOARD_URL=https://fidj.ovh\nAPP_TITLE=${title}\nAPP_VERSION=${releaseVersion}\nPORT=8200\nHOST=127.0.0.1\nLOCAL_DEMO=false\nFIDJ_DATA_DIR=../.fidj-data/${name}\nFIDJ_PRIVACY_ADAPTER_KEY=\n`;
|
|
172
|
+
const settings = `FIDJ_APP_ID=${options.appId}\nFIDJ_API_ENDPOINT=${api.href.replace(/\/$/, "")}\n${issuerSetting}FIDJ_DASHBOARD_URL=https://fidj.ovh\nAPP_TITLE=${title}\nAPP_VERSION=${releaseVersion}\nFIDJ_SIGNIN=${signin}\nPORT=8200\nHOST=127.0.0.1\nLOCAL_DEMO=false\nFIDJ_DATA_DIR=../.fidj-data/${name}\nFIDJ_PRIVACY_ADAPTER_KEY=\n`;
|
|
161
173
|
const localApi = options.local
|
|
162
174
|
? "http://localhost:3201/v3"
|
|
163
175
|
: api.href.replace(/\/$/, "");
|
|
@@ -175,7 +187,7 @@ function scaffold(destination, options) {
|
|
|
175
187
|
releaseVersion,
|
|
176
188
|
localDemo: Boolean(options.local),
|
|
177
189
|
allowAnonymous: anonymous === true || anonymous === "true",
|
|
178
|
-
|
|
190
|
+
signin,
|
|
179
191
|
welcome: options.welcome || title,
|
|
180
192
|
description:
|
|
181
193
|
options.description ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ofidj/generator-fidj",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.1",
|
|
4
4
|
"description": "Generate a TypeScript app with Fidj sign-in, live server-side roles and per-app privacy.",
|
|
5
5
|
"homepage": "https://fidj.ovh",
|
|
6
6
|
"bugs": {
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"yeoman-generator": "^4.12.0"
|
|
24
24
|
},
|
|
25
|
-
"devDependencies": {
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"knip": "^6.35.1"
|
|
27
|
+
},
|
|
26
28
|
"eslintConfig": {
|
|
27
29
|
"extends": "xo-space",
|
|
28
30
|
"env": {
|
|
@@ -31,7 +33,8 @@
|
|
|
31
33
|
},
|
|
32
34
|
"repository": "https://github.com/ofidj/generator-fidj.git",
|
|
33
35
|
"scripts": {
|
|
34
|
-
"test": "node --test test/*.test.cjs"
|
|
36
|
+
"test": "node --test test/*.test.cjs",
|
|
37
|
+
"bp:dead-code": "knip"
|
|
35
38
|
},
|
|
36
39
|
"license": "MIT",
|
|
37
40
|
"bin": {
|