@ofidj/generator-fidj 1.0.0 → 1.0.3
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 +136 -57
- package/bin/create-fidj.cjs +62 -0
- package/generators/app/index.js +100 -189
- package/generators/app/templates/typescript/README.md +82 -0
- package/generators/app/templates/typescript/app.config.json +14 -0
- package/generators/app/templates/typescript/gitignore +5 -0
- package/generators/app/templates/typescript/package.json +25 -0
- package/generators/app/templates/typescript/public/fidj-logo.png +0 -0
- package/generators/app/templates/typescript/public/fonts/IBMPlexMono-400-latin-ext.woff2 +0 -0
- package/generators/app/templates/typescript/public/fonts/IBMPlexMono-400-latin.woff2 +0 -0
- package/generators/app/templates/typescript/public/fonts/IBMPlexMono-500-latin-ext.woff2 +0 -0
- package/generators/app/templates/typescript/public/fonts/IBMPlexMono-500-latin.woff2 +0 -0
- package/generators/app/templates/typescript/public/fonts/IBMPlexSans-400_600-latin-ext.woff2 +0 -0
- package/generators/app/templates/typescript/public/fonts/IBMPlexSans-400_600-latin.woff2 +0 -0
- package/generators/app/templates/typescript/public/fonts/InstrumentSerif-400-latin-ext.woff2 +0 -0
- package/generators/app/templates/typescript/public/fonts/InstrumentSerif-400-latin.woff2 +0 -0
- package/generators/app/templates/typescript/public/index.html +16 -0
- package/generators/app/templates/typescript/scripts/build.mjs +49 -0
- package/generators/app/templates/typescript/scripts/privacy-check.mjs +21 -0
- package/generators/app/templates/typescript/scripts/render-content.mjs +14 -0
- package/generators/app/templates/typescript/server/data-store.ts +123 -0
- package/generators/app/templates/typescript/server/index.ts +338 -0
- package/generators/app/templates/typescript/server/privacy-adapter.ts +21 -0
- package/generators/app/templates/typescript/src/content.ts +491 -0
- package/generators/app/templates/typescript/src/fonts.css +92 -0
- package/generators/app/templates/typescript/src/main.ts +251 -0
- package/generators/app/templates/typescript/src/service-agreement.ts +40 -0
- package/generators/app/templates/typescript/src/style.css +916 -0
- package/generators/app/templates/typescript/src/tokens.css +56 -0
- package/generators/app/templates/typescript/test/server.test.mjs +287 -0
- package/generators/app/templates/typescript/tsconfig.json +15 -0
- package/lib/app-module.cjs +64 -0
- package/lib/scaffold.cjs +214 -0
- package/package.json +15 -13
- package/generators/app/templates/app2018/.travis.yml +0 -35
- package/generators/app/templates/app2018/LICENSE +0 -21
- package/generators/app/templates/app2018/Procfile +0 -1
- package/generators/app/templates/app2018/README.md +0 -49
- package/generators/app/templates/app2018/config.xml +0 -91
- package/generators/app/templates/app2018/gulpfile.js +0 -63
- package/generators/app/templates/app2018/ionic.config.json +0 -9
- package/generators/app/templates/app2018/package.json +0 -90
- package/generators/app/templates/app2018/platforms/README.md +0 -1
- package/generators/app/templates/app2018/resources/README.md +0 -6
- package/generators/app/templates/app2018/resources/icon.png +0 -0
- package/generators/app/templates/app2018/resources/marketing-01-ipad.png +0 -0
- package/generators/app/templates/app2018/resources/marketing-01-iphone.png +0 -0
- package/generators/app/templates/app2018/resources/marketing-02-ipad.png +0 -0
- package/generators/app/templates/app2018/resources/marketing-02-iphone.png +0 -0
- package/generators/app/templates/app2018/resources/marketing-03-ipad.png +0 -0
- package/generators/app/templates/app2018/resources/marketing-03-iphone.png +0 -0
- package/generators/app/templates/app2018/resources/marketing.json +0 -19
- package/generators/app/templates/app2018/resources/splash.png +0 -0
- package/generators/app/templates/app2018/src/app/app.component.ts +0 -88
- package/generators/app/templates/app2018/src/app/app.html +0 -19
- package/generators/app/templates/app2018/src/app/app.module.ts +0 -40
- package/generators/app/templates/app2018/src/app/app.scss +0 -16
- package/generators/app/templates/app2018/src/app/main.ts +0 -5
- package/generators/app/templates/app2018/src/app/shared/card.model.ts +0 -10
- package/generators/app/templates/app2018/src/app/shared/profile.service.ts +0 -46
- package/generators/app/templates/app2018/src/app/shared/shared.module.ts +0 -35
- package/generators/app/templates/app2018/src/app/shared/storage.service.ts +0 -11
- package/generators/app/templates/app2018/src/app/shared/version.const.ts +0 -5
- package/generators/app/templates/app2018/src/assets/icon/favicon.ico +0 -0
- package/generators/app/templates/app2018/src/assets/imgs/gg.png +0 -0
- package/generators/app/templates/app2018/src/assets/imgs/github.png +0 -0
- package/generators/app/templates/app2018/src/assets/imgs/login.svg +0 -5668
- package/generators/app/templates/app2018/src/assets/imgs/logo.png +0 -0
- package/generators/app/templates/app2018/src/index.html +0 -49
- package/generators/app/templates/app2018/src/manifest.json +0 -13
- package/generators/app/templates/app2018/src/pages/home/home.html +0 -56
- package/generators/app/templates/app2018/src/pages/home/home.scss +0 -3
- package/generators/app/templates/app2018/src/pages/home/home.ts +0 -126
- package/generators/app/templates/app2018/src/pages/login/login.html +0 -56
- package/generators/app/templates/app2018/src/pages/login/login.scss +0 -44
- package/generators/app/templates/app2018/src/pages/login/login.ts +0 -59
- package/generators/app/templates/app2018/src/pages/profile/profile.html +0 -33
- package/generators/app/templates/app2018/src/pages/profile/profile.scss +0 -3
- package/generators/app/templates/app2018/src/pages/profile/profile.ts +0 -36
- package/generators/app/templates/app2018/src/service-worker.js +0 -31
- package/generators/app/templates/app2018/src/theme/variables.scss +0 -88
- package/generators/app/templates/app2018/tests/karma.unit-tests.conf.js +0 -84
- package/generators/app/templates/app2018/tests/unit/app.test.js +0 -10
- package/generators/app/templates/app2018/tsconfig.json +0 -28
- package/generators/app/templates/app2018/tslint.json +0 -8
- package/generators/app/templates/app2018/web.js +0 -21
- package/generators/app/templates/app2021/.travis.yml +0 -32
- package/generators/app/templates/app2021/LICENSE +0 -21
- package/generators/app/templates/app2021/Procfile +0 -1
- package/generators/app/templates/app2021/README.md +0 -45
- package/generators/app/templates/app2021/angular.json +0 -188
- package/generators/app/templates/app2021/browserslist +0 -12
- package/generators/app/templates/app2021/ionic.config.json +0 -9
- package/generators/app/templates/app2021/karma.conf.js +0 -31
- package/generators/app/templates/app2021/package-lock.json +0 -5
- package/generators/app/templates/app2021/package.json +0 -77
- package/generators/app/templates/app2021/platforms/README.md +0 -1
- package/generators/app/templates/app2021/resources/README.md +0 -6
- package/generators/app/templates/app2021/resources/icon.png +0 -0
- package/generators/app/templates/app2021/resources/marketing-01-ipad.png +0 -0
- package/generators/app/templates/app2021/resources/marketing-01-iphone.png +0 -0
- package/generators/app/templates/app2021/resources/marketing-02-ipad.png +0 -0
- package/generators/app/templates/app2021/resources/marketing-02-iphone.png +0 -0
- package/generators/app/templates/app2021/resources/marketing-03-ipad.png +0 -0
- package/generators/app/templates/app2021/resources/marketing-03-iphone.png +0 -0
- package/generators/app/templates/app2021/resources/marketing.json +0 -19
- package/generators/app/templates/app2021/resources/splash.png +0 -0
- package/generators/app/templates/app2021/src/app/app-routing.module.ts +0 -25
- package/generators/app/templates/app2021/src/app/app.component.html +0 -3
- package/generators/app/templates/app2021/src/app/app.component.scss +0 -0
- package/generators/app/templates/app2021/src/app/app.component.spec.ts +0 -50
- package/generators/app/templates/app2021/src/app/app.component.ts +0 -28
- package/generators/app/templates/app2021/src/app/app.module.ts +0 -24
- package/generators/app/templates/app2021/src/app/content/content-routing.module.ts +0 -16
- package/generators/app/templates/app2021/src/app/content/content.module.ts +0 -20
- package/generators/app/templates/app2021/src/app/content/content.page.html +0 -31
- package/generators/app/templates/app2021/src/app/content/content.page.scss +0 -0
- package/generators/app/templates/app2021/src/app/content/content.page.ts +0 -26
- package/generators/app/templates/app2021/src/app/explore-container/explore-container.component.html +0 -4
- package/generators/app/templates/app2021/src/app/explore-container/explore-container.component.scss +0 -4
- package/generators/app/templates/app2021/src/app/explore-container/explore-container.component.spec.ts +0 -24
- package/generators/app/templates/app2021/src/app/explore-container/explore-container.component.ts +0 -16
- package/generators/app/templates/app2021/src/app/explore-container/explore-container.module.ts +0 -14
- package/generators/app/templates/app2021/src/app/my/my-routing.module.ts +0 -36
- package/generators/app/templates/app2021/src/app/my/my.module.ts +0 -19
- package/generators/app/templates/app2021/src/app/my/my.page.html +0 -15
- package/generators/app/templates/app2021/src/app/my/my.page.scss +0 -1
- package/generators/app/templates/app2021/src/app/my/my.page.spec.ts +0 -26
- package/generators/app/templates/app2021/src/app/my/my.page.ts +0 -25
- package/generators/app/templates/app2021/src/app/profile/profile-routing.module.ts +0 -16
- package/generators/app/templates/app2021/src/app/profile/profile.module.ts +0 -20
- package/generators/app/templates/app2021/src/app/profile/profile.page.html +0 -49
- package/generators/app/templates/app2021/src/app/profile/profile.page.scss +0 -0
- package/generators/app/templates/app2021/src/app/profile/profile.page.spec.ts +0 -25
- package/generators/app/templates/app2021/src/app/profile/profile.page.ts +0 -44
- package/generators/app/templates/app2021/src/app/shared/const.ts +0 -3
- package/generators/app/templates/app2021/src/app/signin/signin-routing.module.ts +0 -16
- package/generators/app/templates/app2021/src/app/signin/signin.module.ts +0 -18
- package/generators/app/templates/app2021/src/app/signin/signin.page.html +0 -53
- package/generators/app/templates/app2021/src/app/signin/signin.page.scss +0 -36
- package/generators/app/templates/app2021/src/app/signin/signin.page.ts +0 -69
- package/generators/app/templates/app2021/src/assets/icon/favicon.png +0 -0
- package/generators/app/templates/app2021/src/assets/shapes.svg +0 -1
- package/generators/app/templates/app2021/src/environments/environment.prod.ts +0 -3
- package/generators/app/templates/app2021/src/environments/environment.ts +0 -16
- package/generators/app/templates/app2021/src/global.scss +0 -26
- package/generators/app/templates/app2021/src/index.html +0 -26
- package/generators/app/templates/app2021/src/main.ts +0 -12
- package/generators/app/templates/app2021/src/polyfills.ts +0 -67
- package/generators/app/templates/app2021/src/test.ts +0 -20
- package/generators/app/templates/app2021/src/theme/variables.scss +0 -232
- package/generators/app/templates/app2021/src/zone-flags.ts +0 -5
- package/generators/app/templates/app2021/tests/protractor.conf.js +0 -36
- package/generators/app/templates/app2021/tests/src/app.e2e-spec.ts +0 -14
- package/generators/app/templates/app2021/tests/src/app.po.ts +0 -11
- package/generators/app/templates/app2021/tests/tsconfig.json +0 -13
- package/generators/app/templates/app2021/tsconfig.app.json +0 -18
- package/generators/app/templates/app2021/tsconfig.json +0 -23
- package/generators/app/templates/app2021/tsconfig.spec.json +0 -18
- package/generators/app/templates/app2021/tslint.json +0 -149
- package/generators/app/templates/app2021/web.js +0 -21
package/README.md
CHANGED
|
@@ -1,74 +1,153 @@
|
|
|
1
1
|
# @ofidj/generator-fidj
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[![Build Status][travis-image]][travis-url]
|
|
3
|
+
Generate a Fidj-integrated TypeScript app with a short command. Public identity and content are generator inputs; authentication and per-app privacy are reusable template behavior. Node 22 or 24 is the validation matrix.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
>
|
|
8
|
-
> _Renamed from `generator-fidj` at 1.0.0. The old `generator-fidj@3.x` is deprecated. The yeoman command becomes `yo @ofidj/fidj` (note the `@ofidj/` prefix)._
|
|
5
|
+
Registry examples require published compatible versions. For current workspace development, supply a built local SDK with `--sdk-path`.
|
|
9
6
|
|
|
10
|
-
##
|
|
7
|
+
## Content app — static hosting
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
```sh
|
|
10
|
+
npx -y -p @ofidj/generator-fidj -p yo yo @ofidj/fidj my-app --app-id YOUR_FIDJ_ID --title "My App" --welcome "Welcome" --content "<p>About my app</p>" --domain example.com
|
|
11
|
+
cd my-app
|
|
12
|
+
npm install && npm run build-prod
|
|
13
|
+
```
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
Serve `www/` on a static host. It contains your public content, SDK sign-in/account creation, current roles, consent/history, scoped export and confirmed departure, plus `CNAME`. Private signing keys are never needed in the browser. `npm start` supplies an optional Node preview; this mode makes its API calls directly to Fidj.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm install -g yo
|
|
19
|
-
npm install -g @ionic/cli
|
|
20
|
-
npm install -g @angular/cli
|
|
21
|
-
npm install -g @ofidj/generator-fidj
|
|
22
|
-
```
|
|
23
|
-
Then generate your new project:
|
|
24
|
-
```bash
|
|
25
|
-
yo @ofidj/fidj
|
|
26
|
-
```
|
|
27
|
-
or as an expert :
|
|
28
|
-
```bash
|
|
29
|
-
yo @ofidj/fidj my-app app2021 your-app-id "" me-as-user "my description" \
|
|
30
|
-
"Welcome there" \
|
|
31
|
-
"Hey<br>Heres my content: <img src=blank /><br> \o/ "
|
|
32
|
-
```
|
|
17
|
+
`--content` is trusted developer-authored HTML (like any checked-in application source), never untrusted visitor input. Set real terms before release; the generated agreement is explicitly a demo. The content app has no independent user database. Erasure/export cover Fidj-held membership records, and pending cleanup stays pending.
|
|
33
18
|
|
|
34
|
-
##
|
|
19
|
+
## Backend example — protected actions
|
|
35
20
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
npm install -g @ionic/cli
|
|
40
|
-
npm install -g @angular/cli
|
|
41
|
-
```
|
|
21
|
+
Omit `--content` to generate Studio Notes: a browser client plus Node backend with private persisted notes. The backend revalidates the session and live app roles for every protected request. Owner/Editor may write; other members may read their own notes. This mode requires Node hosting and is separate from the static content app scenario.
|
|
22
|
+
|
|
23
|
+
Departure uses an in-app confirmation that states the membership, consent and notes being removed and preserves the membership when cancelled.
|
|
42
24
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
25
|
+
Configure the generated privacy adapter so departure from either Studio Notes or Fidj erases the same app data. Notes persist outside disposable output. Failed cleanup stays pending in Fidj and can be retried; completed receipts remain visible. See the generated README for server-side configuration and persistence limits.
|
|
26
|
+
|
|
27
|
+
## CLI and local development
|
|
28
|
+
|
|
29
|
+
The public entry is `yo @ofidj/fidj`; output goes to `<cwd>/<appname>`. Use the `npx` form above or install once:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
npm install --no-save @ofidj/generator-fidj yo
|
|
33
|
+
npx --no-install yo @ofidj/fidj my-app --app-id YOUR_FIDJ_ID
|
|
48
34
|
```
|
|
49
35
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
36
|
+
Repeat a flag for `--highlight` and `--badge`; the values are read from the
|
|
37
|
+
command line, so a comma inside one is safe. The old `app2021` positional command
|
|
38
|
+
and its Ionic/Cordova templates are gone; the `typescript` template is the only
|
|
39
|
+
one.
|
|
40
|
+
|
|
41
|
+
> Maintainers: `bin/create-fidj.cjs` is the same scaffolder without Yeoman, for
|
|
42
|
+
> tooling that runs from a checkout and cannot install `yo` —
|
|
43
|
+
> `scripts/local-stack.py` and `fidj-app`'s `create:local` use it.
|
|
44
|
+
> `test/parity.test.cjs` fails if either door ever gains an input the other
|
|
45
|
+
> lacks. It is not an entry point to hand to anyone building an app.
|
|
46
|
+
|
|
47
|
+
- `--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
|
+
- `--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
|
+
- `--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.
|
|
50
|
+
- `--logo <image>` and `--favicon <image>`: the app's own marks, copied into `public/brand/`. The logo sits beside the app name at the top of the sign-in panel; the favicon goes in the browser tab. Both fall back to the Fidj mark, so neither is ever blank. Accepts `.png`, `.svg`, `.gif`, `.jpg`, `.webp` or `.ico` under 512KB — an animated GIF works as a logo, and as a favicon in the browsers that animate one.
|
|
51
|
+
- `--api-endpoint`: select the API (default: hosted sandbox).
|
|
52
|
+
- `--sdk-path` or `FIDJ_SDK_DIR`: use a built local SDK during coordinated development.
|
|
53
|
+
- `--local` or `FIDJ_LOCAL=true`: use the loopback API/console. Test credentials stay in the validation guide, outside the content app UI. Supply the matching local app ID; `FIDJ_APP_ID` can override it.
|
|
54
|
+
- `--replace`: regenerate only a destination containing `.fidj-generated`; unmarked projects are protected.
|
|
55
|
+
|
|
56
|
+
The generated shell paints from one design system: `src/tokens.css` holds every
|
|
57
|
+
colour, family and radius, and `src/style.css` may not introduce a literal of
|
|
58
|
+
its own. Fonts are self-hosted under `public/fonts` and served from the build
|
|
59
|
+
manifest, so a generated site stays statically hostable and makes no
|
|
60
|
+
third-party request on sign-in. fidj-app consumes the same tokens, which is why
|
|
61
|
+
a style change lands here first.
|
|
62
|
+
|
|
63
|
+
Generation writes `.env.example`, `.env` and public `app.config.json`. Static configuration is embedded at build time: regenerate/rebuild when changing endpoints. Notes server configuration is read at runtime. Do not place secrets in any public configuration or content input.
|
|
64
|
+
|
|
65
|
+
For unpublished coordinated changes, build the sibling SDK and pass `--sdk-path` with its absolute `dist` path. Committed templates use registry dependency `@ofidj/node ^3.6.24`; a registry-only install is not validated until coordinated versions are published.
|
|
66
|
+
|
|
67
|
+
## Real validation repository
|
|
68
|
+
|
|
69
|
+
[mleweb](https://github.com/mlefree/mleweb) is the thin command-line validation fixture whose generated website is mlefree.com. Its `package.json` passes the original Mario GIF, welcome text and About/CV/contact links directly into this generator. No custom downstream renderer is required. GitHub CI runs the same generation/build path on Node 22/24 and uploads artifacts. Publishing the site to gh-pages, which is what mlefree.com serves, is a separate deliberate job: it runs on mleweb's `master` or on an explicit workflow dispatch, never from a version branch.
|
|
70
|
+
|
|
71
|
+
Run `npm test` for scaffolding/CLI safety tests. Generated projects include TypeScript checking and HTTP integration tests for live authorization and privacy isolation. See the [local acceptance walkthrough](../LOCAL-DEVELOPMENT.md). Follow TDD: add/run a failing generator or generated-app test before implementation, make it green, then refactor and rerun regression checks.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## Releasing to npm
|
|
75
|
+
|
|
76
|
+
Pushing to the `package` branch publishes the package, the same convention the
|
|
77
|
+
SDK and contracts repositories use. CI installs, runs the tests and publishes
|
|
78
|
+
with `NPM_TOKEN`; this package publishes from the repository root rather than
|
|
79
|
+
from a `dist` directory, because it ships its sources.
|
|
80
|
+
|
|
81
|
+
The package contains `bin/`, `lib/` and the `typescript` template only. The
|
|
82
|
+
`app2018` and `app2021` Yeoman templates were deleted: nothing read them, and
|
|
83
|
+
they accounted for 11.8MB of the repository. `main` points at
|
|
84
|
+
`lib/scaffold.cjs`, so `require('@ofidj/generator-fidj')` returns `{ scaffold }`
|
|
85
|
+
alongside the `create-fidj` binary.
|
|
86
|
+
|
|
87
|
+
Publish contracts before the SDK, and the SDK before anything that resolves it
|
|
88
|
+
from the registry: the SDK depends on `@ofidj/contracts` by range, so a build
|
|
89
|
+
picks up whatever is published at the time.
|
|
56
90
|
|
|
57
|
-
|
|
58
|
-
|
|
91
|
+
## Entry flow
|
|
92
|
+
|
|
93
|
+
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.
|
|
94
|
+
|
|
95
|
+
## Compose an existing app as a module
|
|
96
|
+
|
|
97
|
+
Build your application for the `/module/` base URL, then pass its public output to the same generator:
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
npx -y -p @ofidj/generator-fidj -p yo yo @ofidj/fidj my-app --app-id YOUR_FIDJ_ID --title "My App" --welcome "Welcome back" --description "Your app description" --anonymous false --module ./built-console --module-entry 'index.html#/my'
|
|
101
|
+
cd my-app && npm install && npm run build-prod
|
|
59
102
|
```
|
|
60
103
|
|
|
61
|
-
|
|
104
|
+
The result is one static website: the generated SDK sign-in entry and the module under `www/module/`. After login, the entry opens the module on the same origin. The module must use the same public app ID/API and independently validate authorization for its own operations. The generator copies public build assets without changing their source; module input cannot contain environment files, dependency directories or symlinks.
|
|
105
|
+
|
|
106
|
+
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.
|
|
107
|
+
|
|
108
|
+
The module entry receives a `meta[name="fidj-signin"]` URL, relative to its base URL. A module can send its sign-in, logout or expired-session flow there. Fidj's console implements this handoff and preserves departure status. The generated preview serves module assets from an explicit build manifest, including directory index URLs.
|
|
109
|
+
|
|
110
|
+
`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.
|
|
111
|
+
|
|
112
|
+
## Generated account lifecycle
|
|
113
|
+
|
|
114
|
+
Content and module entries include `#/forgot`, `#/reset`, `#/verify`, and authenticated `#/account` screens in the same split layout as sign-in. These routes remain in the generated shell when an application module is attached. Forgot-password responses are neutral; verification requires an explicit click. `My account` provides verification status and resend. Password reset affects the shared Fidj identity across apps. Configure the API’s trusted account UI URL for email links; the current generated API integration requires the coordinated 3.6.24 SDK/API. The server-backed Notes template remains a separate integration example.
|
|
115
|
+
|
|
116
|
+
## App data adapter
|
|
117
|
+
|
|
118
|
+
The Notes server exposes `POST /fidj/privacy` for HMAC-authenticated, timestamped export/erase operations. Set a private `FIDJ_PRIVACY_ADAPTER_KEY` and register the same app ID, endpoint and key in the API’s server-side `FIDJ_PRIVACY_ADAPTERS`. Static content apps need no independent data adapter. `FIDJ_DATA_DIR` must stay outside `www`, `dist` and disposable generated output; the generated environment defaults to a sibling data directory.
|
|
119
|
+
|
|
120
|
+
The file adapter uses atomic writes and a serialized queue for one writer process. Replace it with a transactional database for multi-instance hosting. Erasure receipts retain a hashed subject for replay protection, pruned after 30 days on the next write. Backups and unregistered stores are not covered. Tests exercise live authorization, restart persistence, private exports, adapter authentication, failed cleanup and duplicate-request safety.
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
### Check a registered data handler
|
|
124
|
+
|
|
125
|
+
Generated starters include `npm run privacy:check`: configure the server-only `FIDJ_PRIVACY_ADAPTER_KEY` in `.env`, start the app, then run the command. It signs a readiness request, verifies capabilities and probes durable storage without exporting or deleting user records. `FIDJ_APP_URL` can override the default loopback URL. `npm run privacy:rehearse` builds and exercises an isolated temporary data store, including scoped export, failure, retry and persistence. Production app data and credentials are never inputs to that rehearsal.
|
|
126
|
+
|
|
127
|
+
Readiness confirms this handler only. Register its trusted URL and independent secret with the Fidj API operator; the app owner console can then check the same connection. Group roles granted in Fidj are enforced by the generated backend on each protected operation.
|
|
128
|
+
|
|
129
|
+
## Beta OIDC input
|
|
130
|
+
|
|
131
|
+
`--oidc-issuer` selects the OIDC entry for content/module apps while preserving branding and anonymous-entry configuration. This is beta: the API ships an integrated provider from 3.6.26, but it serves no `/oidc` routes until an operator configures an issuer and signing keys, so confirm the issuer you pass actually answers discovery before offering it.
|
|
132
|
+
|
|
133
|
+
A compatible issuer, REST API and registered callback without a fragment are prerequisites. Validate that integration and coordinated SDK publication before offering it to app builders. Notes retains its existing authentication flow; no subject migration is implied.
|
|
134
|
+
|
|
135
|
+
## Required service agreement
|
|
136
|
+
|
|
137
|
+
All generated sign-in and account-creation forms (content, composed Fidj and
|
|
138
|
+
Notes) share an unchecked required agreement checkbox. Both submit buttons stay
|
|
139
|
+
disabled until it is checked. Reading the agreement opens an in-app dialog;
|
|
140
|
+
failed agreement loading keeps sign-in blocked. Anonymous entry, when enabled,
|
|
141
|
+
is not a login and does not record agreement acceptance.
|
|
62
142
|
|
|
63
|
-
|
|
143
|
+
The text and version come from the app's public API metadata, not copied generator
|
|
144
|
+
settings. The API records acceptance before issuing the app token, preserves
|
|
145
|
+
optional choices, and rejects a missing choice or stale version. Repeated login
|
|
146
|
+
with the same version does not duplicate the history. Administrative membership
|
|
147
|
+
creation does not fabricate a user's acceptance.
|
|
64
148
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
[daviddm-url]: https://david-dm.org/ofidj/generator-fidj
|
|
71
|
-
[coveralls-image]: https://coveralls.io/repos/ofidj/generator-fidj/badge.svg
|
|
72
|
-
[coveralls-url]: https://coveralls.io/r/ofidj/generator-fidj
|
|
73
|
-
[codecov-image]: https://codecov.io/gh/ofidj/generator-fidj/branch/master/graph/badge.svg
|
|
74
|
-
[codecov-url]: https://codecov.io/gh/ofidj/generator-fidj
|
|
149
|
+
Before release, the owner sets `configurationAsJSON.serviceAgreement` to
|
|
150
|
+
`{"version":"2026-09-11","text":"Your complete app service agreement"}` through
|
|
151
|
+
`PUT /apps/:appId`, preserving its other configuration fields. Changing the text
|
|
152
|
+
requires a new version. Until configured, the API exposes the explicitly labelled
|
|
153
|
+
`starter-demo-1` demo agreement. No generated-source edit is necessary.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { parseArgs } = require("node:util");
|
|
3
|
+
const { scaffold } = require("../lib/scaffold.cjs");
|
|
4
|
+
try {
|
|
5
|
+
const { values, positionals } = parseArgs({
|
|
6
|
+
allowPositionals: true,
|
|
7
|
+
options: {
|
|
8
|
+
"app-id": { type: "string" },
|
|
9
|
+
"api-endpoint": { type: "string" },
|
|
10
|
+
"oidc-issuer": { type: "string" },
|
|
11
|
+
"sdk-path": { type: "string" },
|
|
12
|
+
title: { type: "string" },
|
|
13
|
+
welcome: { type: "string" },
|
|
14
|
+
description: { type: "string" },
|
|
15
|
+
content: { type: "string" },
|
|
16
|
+
highlight: { type: "string", multiple: true },
|
|
17
|
+
badge: { type: "string", multiple: true },
|
|
18
|
+
logo: { type: "string" },
|
|
19
|
+
favicon: { type: "string" },
|
|
20
|
+
anonymous: { type: "string" },
|
|
21
|
+
domain: { type: "string" },
|
|
22
|
+
module: { type: "string" },
|
|
23
|
+
"module-entry": { type: "string" },
|
|
24
|
+
local: { type: "boolean" },
|
|
25
|
+
replace: { type: "boolean" },
|
|
26
|
+
help: { type: "boolean" },
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
if (values.help || positionals.length !== 1) {
|
|
30
|
+
console.log(
|
|
31
|
+
"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] [--local] [--replace]",
|
|
32
|
+
);
|
|
33
|
+
process.exitCode = values.help ? 0 : 1;
|
|
34
|
+
} else {
|
|
35
|
+
const dir = scaffold(positionals[0], {
|
|
36
|
+
appId: process.env.FIDJ_APP_ID || values["app-id"],
|
|
37
|
+
apiEndpoint: values["api-endpoint"],
|
|
38
|
+
oidcIssuer: values["oidc-issuer"],
|
|
39
|
+
sdkPath: values["sdk-path"] || process.env.FIDJ_SDK_DIR,
|
|
40
|
+
title: values.title,
|
|
41
|
+
welcome: values.welcome,
|
|
42
|
+
description: values.description,
|
|
43
|
+
content: values.content,
|
|
44
|
+
highlights: values.highlight,
|
|
45
|
+
badges: values.badge,
|
|
46
|
+
logo: values.logo,
|
|
47
|
+
favicon: values.favicon,
|
|
48
|
+
anonymous: values.anonymous,
|
|
49
|
+
domain: values.domain,
|
|
50
|
+
module: values.module,
|
|
51
|
+
moduleEntry: values["module-entry"],
|
|
52
|
+
local: values.local || process.env.FIDJ_LOCAL === "true",
|
|
53
|
+
replace: values.replace,
|
|
54
|
+
});
|
|
55
|
+
console.log(
|
|
56
|
+
`Created ${dir}\nNext: copy .env.example to .env, then npm install && npm run build && npm start.`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error(error.message);
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
}
|
package/generators/app/index.js
CHANGED
|
@@ -1,200 +1,111 @@
|
|
|
1
|
-
const Generator = require(
|
|
2
|
-
const
|
|
3
|
-
|
|
1
|
+
const Generator = require("yeoman-generator");
|
|
2
|
+
const { scaffold } = require("../../lib/scaffold.cjs");
|
|
3
|
+
|
|
4
|
+
// Yeoman and bin/create-fidj.cjs are two doors into the same scaffolder, and
|
|
5
|
+
// they must stay at parity: an input added to one and not the other silently
|
|
6
|
+
// disappears for half the users. test/parity.test.cjs fails when they drift.
|
|
7
|
+
const TEXT_OPTIONS = [
|
|
8
|
+
"app-id",
|
|
9
|
+
"api-endpoint",
|
|
10
|
+
"oidc-issuer",
|
|
11
|
+
"sdk-path",
|
|
12
|
+
"title",
|
|
13
|
+
"welcome",
|
|
14
|
+
"description",
|
|
15
|
+
"content",
|
|
16
|
+
"anonymous",
|
|
17
|
+
"domain",
|
|
18
|
+
"module",
|
|
19
|
+
"module-entry",
|
|
20
|
+
"logo",
|
|
21
|
+
"favicon",
|
|
22
|
+
];
|
|
23
|
+
// Repeatable: --highlight twice yields two cells.
|
|
24
|
+
const LIST_OPTIONS = ["highlight", "badge"];
|
|
25
|
+
const FLAG_OPTIONS = ["local", "replace"];
|
|
26
|
+
|
|
27
|
+
// Yeoman joins a repeated flag into one comma-separated string before any type
|
|
28
|
+
// coercion, and splitting that back is wrong: a highlight body legitimately
|
|
29
|
+
// contains commas ("An install, a fidjId, a few lines"). Read the occurrences
|
|
30
|
+
// from argv, which is exactly what was typed, and fall back to Yeoman's value
|
|
31
|
+
// when the generator is composed programmatically rather than from a command
|
|
32
|
+
// line.
|
|
33
|
+
function repeated(name, fallback, argv = process.argv) {
|
|
34
|
+
const found = [];
|
|
35
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
36
|
+
if (argv[index] === "--" + name && index + 1 < argv.length)
|
|
37
|
+
found.push(argv[index + 1]);
|
|
38
|
+
else if (argv[index].startsWith("--" + name + "="))
|
|
39
|
+
found.push(argv[index].slice(name.length + 3));
|
|
40
|
+
}
|
|
41
|
+
if (found.length) return found;
|
|
42
|
+
if (fallback === undefined) return undefined;
|
|
43
|
+
return Array.isArray(fallback) ? fallback : [fallback];
|
|
44
|
+
}
|
|
4
45
|
|
|
5
46
|
module.exports = class extends Generator {
|
|
6
47
|
constructor(args, opts) {
|
|
7
48
|
super(args, opts);
|
|
8
|
-
|
|
9
|
-
this.
|
|
10
|
-
this.
|
|
11
|
-
|
|
12
|
-
this.option(
|
|
13
|
-
|
|
14
|
-
|
|
49
|
+
this.argument("appname", { type: String, required: false });
|
|
50
|
+
for (const key of TEXT_OPTIONS) this.option(key, { type: String });
|
|
51
|
+
for (const key of LIST_OPTIONS) this.option(key, { type: String });
|
|
52
|
+
for (const key of FLAG_OPTIONS) this.option(key, { type: Boolean });
|
|
53
|
+
this.option("api-endpoint", {
|
|
54
|
+
type: String,
|
|
55
|
+
default: "https://api.sandbox.fidj.ovh/v3",
|
|
15
56
|
});
|
|
16
|
-
|
|
17
|
-
this.argument('appname', {type: String, required: false, desc: 'Your app name'});
|
|
18
|
-
this.argument('appType', {type: String, required: false, desc: 'For ex: "app2021" '});
|
|
19
|
-
this.argument('appId', {type: String, required: false});
|
|
20
|
-
this.argument('appOptions', {type: String, required: false});
|
|
21
|
-
this.argument('appUserName', {type: String, required: false});
|
|
22
|
-
this.argument('appDescription', {type: String, required: false});
|
|
23
|
-
this.argument('appWelcome', {type: String, required: false});
|
|
24
|
-
this.argument('appContent', {type: String, required: false});
|
|
25
57
|
}
|
|
26
|
-
|
|
27
|
-
async initializing() {
|
|
28
|
-
this.pkg = require('../../package.json');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
58
|
async prompting() {
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
this.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
type: 'input',
|
|
55
|
-
name: 'appUserName',
|
|
56
|
-
message: 'Your (github) user name'
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
type: 'input',
|
|
60
|
-
name: 'appName',
|
|
61
|
-
message: 'Your app name (as shown on device)',
|
|
62
|
-
default: this.appname.replace(/[^\w\s]/gi, '').replace(/\s/g, '_').replace(/\s+/g, '').replace(/[\. ,:-]+/g, '').toLowerCase()
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
type: 'input',
|
|
66
|
-
name: 'appDescription',
|
|
67
|
-
message: 'App description',
|
|
68
|
-
default: this.appname
|
|
69
|
-
}
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
this.props = await this.prompt(prompts);
|
|
73
|
-
} else {
|
|
74
|
-
this.props = {
|
|
75
|
-
appName: this.options.appname,
|
|
76
|
-
appUserName: this.options.appUserName,
|
|
77
|
-
appDescription: this.options.appDescription,
|
|
78
|
-
appWelcome: this.options.appWelcome || '<h1>Welcome</h1>',
|
|
79
|
-
appContent: this.options.appContent || '<h1>Hello</h1>'
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const pack = this.props.appUserName.replace(/[^\w\s]/gi, '').replace(/\s+/g, '').replace(/[\. ,:-]+/g, '').toLowerCase();
|
|
84
|
-
const age = this.props.appName.replace(/[^\w\s]/gi, '').replace(/\s/g, '_').replace(/\s+/g, '').replace(/[\. ,:-]+/g, '').toLowerCase();
|
|
85
|
-
const packageDefault = 'ovh.' + (pack ? pack : 'fidj') + '.' + age;
|
|
86
|
-
|
|
87
|
-
if (!this.options.appId) {
|
|
88
|
-
const prompts02 = [{
|
|
89
|
-
type: 'input',
|
|
90
|
-
name: 'appId',
|
|
91
|
-
message: 'Your app Id'
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
type: 'input',
|
|
95
|
-
name: 'appPackage',
|
|
96
|
-
message: 'Your app package name',
|
|
97
|
-
default: packageDefault
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
type: 'input',
|
|
101
|
-
name: 'appHomepage',
|
|
102
|
-
message: 'Home page url',
|
|
103
|
-
default: 'https://fidj.ovh/_/' + age
|
|
104
|
-
}
|
|
105
|
-
];
|
|
106
|
-
|
|
107
|
-
this.props02 = await this.prompt(prompts02);
|
|
108
|
-
} else {
|
|
109
|
-
this.props02 = {
|
|
110
|
-
appId: this.options.appId,
|
|
111
|
-
appPackage: packageDefault,
|
|
112
|
-
appHomepage: 'https://fidj.ovh/_/' + age
|
|
113
|
-
};
|
|
114
|
-
}
|
|
59
|
+
this.answers = await this.prompt([
|
|
60
|
+
...(!this.options.appname
|
|
61
|
+
? [
|
|
62
|
+
{
|
|
63
|
+
type: "input",
|
|
64
|
+
name: "name",
|
|
65
|
+
message: "Project name",
|
|
66
|
+
default: "my-fidj-app",
|
|
67
|
+
},
|
|
68
|
+
]
|
|
69
|
+
: []),
|
|
70
|
+
...(!this.options["app-id"]
|
|
71
|
+
? [
|
|
72
|
+
{
|
|
73
|
+
type: "input",
|
|
74
|
+
name: "appId",
|
|
75
|
+
message: "Public fidjId from your owner console",
|
|
76
|
+
},
|
|
77
|
+
]
|
|
78
|
+
: []),
|
|
79
|
+
]);
|
|
115
80
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
this.
|
|
125
|
-
|
|
126
|
-
this.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
appOptions: this.options.appOptions ? ', {' + this.options.appOptions + '}' : '',
|
|
143
|
-
appVersion: version,
|
|
144
|
-
appKeywords: 'fidj',
|
|
145
|
-
appYear: now.getFullYear(),
|
|
146
|
-
appColor: 'white'
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
this.fs.copyTpl(
|
|
150
|
-
this.templatePath(this.templateName + '/*'),
|
|
151
|
-
this.destinationPath(),
|
|
152
|
-
appArguments
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
this.fs.copyTpl(
|
|
156
|
-
this.templatePath(this.templateName + '/.*'),
|
|
157
|
-
this.destinationPath(),
|
|
158
|
-
appArguments
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
this.fs.copyTpl(
|
|
162
|
-
this.templatePath(this.templateName + '/src'),
|
|
163
|
-
this.destinationPath('src'),
|
|
164
|
-
appArguments
|
|
81
|
+
writing() {
|
|
82
|
+
const name = this.options.appname || this.answers.name;
|
|
83
|
+
scaffold(this.destinationPath(name), {
|
|
84
|
+
name,
|
|
85
|
+
title: this.options.title,
|
|
86
|
+
welcome: this.options.welcome,
|
|
87
|
+
description: this.options.description,
|
|
88
|
+
content: this.options.content,
|
|
89
|
+
anonymous: this.options.anonymous,
|
|
90
|
+
highlights: repeated("highlight", this.options.highlight),
|
|
91
|
+
badges: repeated("badge", this.options.badge),
|
|
92
|
+
logo: this.options.logo,
|
|
93
|
+
favicon: this.options.favicon,
|
|
94
|
+
module: this.options.module,
|
|
95
|
+
moduleEntry: this.options["module-entry"],
|
|
96
|
+
domain: this.options.domain,
|
|
97
|
+
sdkPath: this.options["sdk-path"] || process.env.FIDJ_SDK_DIR,
|
|
98
|
+
oidcIssuer: this.options["oidc-issuer"],
|
|
99
|
+
local: this.options.local || process.env.FIDJ_LOCAL === "true",
|
|
100
|
+
replace: this.options.replace,
|
|
101
|
+
appId:
|
|
102
|
+
process.env.FIDJ_APP_ID || this.options["app-id"] || this.answers.appId,
|
|
103
|
+
apiEndpoint: this.options["api-endpoint"],
|
|
104
|
+
});
|
|
105
|
+
this.log(
|
|
106
|
+
`Created ${name}. Copy .env.example to .env, then npm install && npm run build && npm start.`,
|
|
165
107
|
);
|
|
166
|
-
|
|
167
|
-
if (copyAll) {
|
|
168
|
-
this.fs.copy(
|
|
169
|
-
this.templatePath(this.templateName + '/platforms'),
|
|
170
|
-
this.destinationPath('platforms')
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
this.fs.copy(
|
|
174
|
-
this.templatePath(this.templateName + '/resources'),
|
|
175
|
-
this.destinationPath('resources')
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
this.fs.copy(
|
|
179
|
-
this.templatePath(this.templateName + '/tests'),
|
|
180
|
-
this.destinationPath('tests')
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
//this.fs.copy(
|
|
184
|
-
// this.templatePath(this.templateName + '/www'),
|
|
185
|
-
// this.destinationPath('www')
|
|
186
|
-
//);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async install() {
|
|
191
|
-
if (!this.hasBeenRecordedInFidjOvh) {
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
this.npmInstall();
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
async end() {
|
|
198
|
-
|
|
199
108
|
}
|
|
200
109
|
};
|
|
110
|
+
|
|
111
|
+
module.exports.repeatedOption = repeated;
|