@ofidj/generator-fidj 3.7.5 → 3.8.0
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 +76 -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
package/README.md
CHANGED
|
@@ -44,6 +44,7 @@ one.
|
|
|
44
44
|
> `test/parity.test.cjs` fails if either door ever gains an input the other
|
|
45
45
|
> lacks. It is not an entry point to hand to anyone building an app.
|
|
46
46
|
|
|
47
|
+
- `--signin button|inline|both`: how the app asks (default: `button`). `button` hands every sign-in to Fidj, which is the only shape where this app never sees a password. `inline` keeps the app's own email-and-password form and no Fidj door, for an owner who has decided their page is to be trusted with the credential. `both` leads with the Fidj door and folds the app's form under it — what mleweb passes. The older `--credentials true|false` still says what it always said (`true` is `both`); `--signin` wins when both are given.
|
|
47
48
|
- `--anonymous true|false`: show or hide anonymous entry in content apps (default: `true`). Set `--anonymous false` for a sign-in-only entry flow, as mleweb does.
|
|
48
49
|
- `--highlight "<heading>|<body>"`: add a numbered cell to the sign-in panel, repeatable up to six. These are the app's own selling points, so an app that passes none simply shows its identity — mleweb passes none, Fidj passes four.
|
|
49
50
|
- `--badge <text>`: add a trust badge under the sign-in form, repeatable up to four, 40 characters each. None are supplied by default — "EU-hosted" or "GDPR art. 17 · 20" are claims about a particular app, not about every app the generator makes.
|
|
@@ -101,6 +102,23 @@ picks up whatever is published at the time.
|
|
|
101
102
|
|
|
102
103
|
The generated content app opens on `/#/signin`. Sign in, or choose **Enter anonymously** when enabled, to open `/#/content`, containing the supplied HTML. Signed-in users can open **My privacy** separately. Sign-out and departure return to the sign-in screen. Anonymous content is public; this navigation flow is not a security boundary for static assets.
|
|
103
104
|
|
|
105
|
+
**The entry leads with the Fidj door, and that door opens a window.** The button
|
|
106
|
+
is the one control on the screen wearing `--fidj-accent`, because it is the one
|
|
107
|
+
that belongs to Fidj rather than to the app — and it is the path where the app
|
|
108
|
+
never sees a password. An app generated with `--signin both` keeps its own
|
|
109
|
+
email-and-password form under an *Inline form* disclosure, with its service
|
|
110
|
+
agreement: opening it folds the Fidj door away, because the two are alternatives
|
|
111
|
+
rather than a list. That agreement gates the app's own door and nothing else, so
|
|
112
|
+
an agreement that fails to load never shuts the Fidj door.
|
|
113
|
+
|
|
114
|
+
Pressing it opens Fidj in a browser window of its own rather than navigating
|
|
115
|
+
away. Fidj's screens are served from another origin and refuse to be framed, so
|
|
116
|
+
a dialog drawn inside the page cannot hold them; a window can, and the page the
|
|
117
|
+
person was reading stays exactly where it was. The window says which app it will
|
|
118
|
+
return them to, hands the answer back when they are done, and closes itself. A
|
|
119
|
+
browser that will not open one falls back to the full-page redirect. Fidj's own
|
|
120
|
+
console takes the same door for the same reason — one journey, three apps.
|
|
121
|
+
|
|
104
122
|
## Compose an existing app as a module
|
|
105
123
|
|
|
106
124
|
Build your application for the `/module/` base URL, then pass its public output to the same generator:
|
|
@@ -114,7 +132,7 @@ The result is one static website: the generated SDK sign-in entry and the module
|
|
|
114
132
|
|
|
115
133
|
Hash routes other than the generated entry/content/privacy views are forwarded to the module, preserving existing public cards and console links. The module remains responsible for guarding private routes.
|
|
116
134
|
|
|
117
|
-
|
|
135
|
+
Sign-in, sign-out and expired sessions belong to the shell, which owns those addresses: a module reaches them by leaving the document — the site root — rather than routing inside it, because the shell starts the module in place and only hears `hashchange`. A departure status travels as `?departure=completed|pending` on that address. Fidj's console implements this handover. The shell also names itself in that document — `meta[name="fidj-shell"]`, holding the base its addresses start from — so a module can offer what only a shell has, such as the account screen at `#/account`; standalone there is no such meta and nothing to offer. The generated preview serves module assets from an explicit build manifest, including directory index URLs, and the generated entry names the module's scripts and stylesheet in its head so the browser starts them without waiting for the shell's session.
|
|
118
136
|
|
|
119
137
|
`fidj-app` now exercises this path with `npm run create:local`: its owner/profile/privacy features remain an explicit Angular console module, while the generator owns the shared entry and final assembly. The local launcher serves the generated Fidj output on port 4200. The source console is maintained outside disposable `.gen` and must be rebuilt before regenerating.
|
|
120
138
|
|
package/bin/create-fidj.cjs
CHANGED
|
@@ -19,6 +19,7 @@ try {
|
|
|
19
19
|
favicon: { type: "string" },
|
|
20
20
|
anonymous: { type: "string" },
|
|
21
21
|
credentials: { type: "string" },
|
|
22
|
+
signin: { type: "string" },
|
|
22
23
|
domain: { type: "string" },
|
|
23
24
|
module: { type: "string" },
|
|
24
25
|
"module-entry": { type: "string" },
|
|
@@ -29,7 +30,7 @@ try {
|
|
|
29
30
|
});
|
|
30
31
|
if (values.help || positionals.length !== 1) {
|
|
31
32
|
console.log(
|
|
32
|
-
"Usage: create-fidj <directory> --app-id <fidjId> [--api-endpoint <url>] [--title <text> --welcome <text> --description <text> --content <html> --domain <hostname>] [--highlight '<heading>|<body>' ...] [--badge <text> ...] [--logo <image>] [--favicon <image>] [--module <built-directory> --module-entry <index.html#/route>] [--oidc-issuer https://api.example/oidc] [--anonymous true|false] [--credentials true|false] [--local] [--replace]",
|
|
33
|
+
"Usage: create-fidj <directory> --app-id <fidjId> [--api-endpoint <url>] [--title <text> --welcome <text> --description <text> --content <html> --domain <hostname>] [--highlight '<heading>|<body>' ...] [--badge <text> ...] [--logo <image>] [--favicon <image>] [--module <built-directory> --module-entry <index.html#/route>] [--oidc-issuer https://api.example/oidc] [--anonymous true|false] [--signin button|inline|both] [--credentials true|false] [--local] [--replace]",
|
|
33
34
|
);
|
|
34
35
|
process.exitCode = values.help ? 0 : 1;
|
|
35
36
|
} else {
|
|
@@ -48,6 +49,7 @@ try {
|
|
|
48
49
|
favicon: values.favicon,
|
|
49
50
|
anonymous: values.anonymous,
|
|
50
51
|
credentials: values.credentials,
|
|
52
|
+
signin: values.signin,
|
|
51
53
|
domain: values.domain,
|
|
52
54
|
module: values.module,
|
|
53
55
|
moduleEntry: values["module-entry"],
|
package/generators/app/index.js
CHANGED
|
@@ -15,6 +15,7 @@ const TEXT_OPTIONS = [
|
|
|
15
15
|
"content",
|
|
16
16
|
"anonymous",
|
|
17
17
|
"credentials",
|
|
18
|
+
"signin",
|
|
18
19
|
"domain",
|
|
19
20
|
"module",
|
|
20
21
|
"module-entry",
|
|
@@ -89,6 +90,7 @@ module.exports = class extends Generator {
|
|
|
89
90
|
content: this.options.content,
|
|
90
91
|
anonymous: this.options.anonymous,
|
|
91
92
|
credentials: this.options.credentials,
|
|
93
|
+
signin: this.options.signin,
|
|
92
94
|
highlights: repeated("highlight", this.options.highlight),
|
|
93
95
|
badges: repeated("badge", this.options.badge),
|
|
94
96
|
logo: this.options.logo,
|
|
@@ -51,7 +51,7 @@ Set `--anonymous false` in the generator command to remove anonymous entry and r
|
|
|
51
51
|
|
|
52
52
|
## Application modules
|
|
53
53
|
|
|
54
|
-
When generated with `--module`, successful sign-in
|
|
54
|
+
When generated with `--module`, successful sign-in starts the copied module at the address configured by `moduleEntry`, inside this document rather than as a second page. Its assets are served from `/module/` on the same origin and it must validate sessions/permissions independently. To return to this shared sign-in page it leaves the document for the site root, optionally with `?departure=completed|pending`. Module code is public static output; user data belongs behind authorized APIs. Rebuild the maintained module source and regenerate to update it; never patch its copied files here.
|
|
55
55
|
|
|
56
56
|
## Connect app data rights
|
|
57
57
|
|
|
@@ -6,9 +6,32 @@ const escape = (value) =>
|
|
|
6
6
|
char
|
|
7
7
|
],
|
|
8
8
|
);
|
|
9
|
+
// The mounted app's scripts are injected by the shell once its own bundle has
|
|
10
|
+
// arrived and run, so the browser's preload scanner never sees them in the
|
|
11
|
+
// document and the larger download waits on the smaller one. Naming them here
|
|
12
|
+
// starts both at once. Preloaded, never applied: the module's stylesheet over
|
|
13
|
+
// the shell's own sign-in screen would be one design on another's markup. An app
|
|
14
|
+
// that carries a module is an app people open to reach that module, so this is
|
|
15
|
+
// worth its bytes there — and an app without one names nothing extra.
|
|
16
|
+
function mountedHead(config) {
|
|
17
|
+
const mount = config.moduleMount;
|
|
18
|
+
if (!mount) return "";
|
|
19
|
+
return [
|
|
20
|
+
// A hosted console cannot tell from its own code that it is hosted, and some
|
|
21
|
+
// of what it shows depends on it: verification and recovery are the shell's
|
|
22
|
+
// own screens. This says a shell is here, and where its addresses start.
|
|
23
|
+
`<meta name="fidj-shell" content="./">`,
|
|
24
|
+
...mount.scripts.map(
|
|
25
|
+
(script) => `<link rel="modulepreload" href="${escape(script.src)}">`,
|
|
26
|
+
),
|
|
27
|
+
...mount.styles.map(
|
|
28
|
+
(href) => `<link rel="preload" as="style" href="${escape(href)}">`,
|
|
29
|
+
),
|
|
30
|
+
].join("");
|
|
31
|
+
}
|
|
9
32
|
export function renderContent(config) {
|
|
10
33
|
// HTML is supplied by the developer at generation time, never by an app visitor.
|
|
11
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="referrer" content="no-referrer"><title>${escape(config.title)}</title><link rel="icon" href="${escape(config.favicon)}"><link rel="stylesheet" href="./main.css"
|
|
12
|
-
<header class="topbar"><a class="brand" href="#/content"><img src="${escape(config.logo)}" alt="">${escape(config.title)}</a></header>
|
|
34
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="referrer" content="no-referrer"><title>${escape(config.title)}</title><link rel="icon" href="${escape(config.favicon)}"><link rel="stylesheet" href="./main.css">${mountedHead(config)}</head><body>
|
|
35
|
+
<header class="topbar"><a class="brand" href="#/content"><img src="${escape(config.logo)}" alt="">${escape(config.title)}</a><nav class="content-nav" id="app-nav" aria-label="App navigation" hidden></nav></header>
|
|
13
36
|
<main><template id="public-content"><section class="card public-content"><h1>${escape(config.welcome)}</h1><div>${config.content}</div></section></template><div id="app" aria-live="polite"></div><footer>Built with Fidj · Your choices belong to this app.</footer></main><script type="module" src="./main.js"></script></body></html>`;
|
|
14
37
|
}
|
|
@@ -333,6 +333,12 @@ if (require.main === module) {
|
|
|
333
333
|
dashboardUrl: process.env.FIDJ_DASHBOARD_URL || "https://fidj.ovh",
|
|
334
334
|
title: process.env.APP_TITLE || "My workspace",
|
|
335
335
|
releaseVersion: process.env.APP_VERSION || "",
|
|
336
|
+
// How this app asks. The generator writes it; anything it does not
|
|
337
|
+
// recognise means the shape that keeps the promise — Fidj asks, and this
|
|
338
|
+
// app never sees a password.
|
|
339
|
+
signin: ["button", "inline", "both"].includes(process.env.FIDJ_SIGNIN || "")
|
|
340
|
+
? (process.env.FIDJ_SIGNIN as "button" | "inline" | "both")
|
|
341
|
+
: "button",
|
|
336
342
|
localDemo:
|
|
337
343
|
process.env.LOCAL_DEMO === "true" &&
|
|
338
344
|
host === "127.0.0.1" &&
|