@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.
Files changed (161) hide show
  1. package/README.md +136 -57
  2. package/bin/create-fidj.cjs +62 -0
  3. package/generators/app/index.js +100 -189
  4. package/generators/app/templates/typescript/README.md +82 -0
  5. package/generators/app/templates/typescript/app.config.json +14 -0
  6. package/generators/app/templates/typescript/gitignore +5 -0
  7. package/generators/app/templates/typescript/package.json +25 -0
  8. package/generators/app/templates/typescript/public/fidj-logo.png +0 -0
  9. package/generators/app/templates/typescript/public/fonts/IBMPlexMono-400-latin-ext.woff2 +0 -0
  10. package/generators/app/templates/typescript/public/fonts/IBMPlexMono-400-latin.woff2 +0 -0
  11. package/generators/app/templates/typescript/public/fonts/IBMPlexMono-500-latin-ext.woff2 +0 -0
  12. package/generators/app/templates/typescript/public/fonts/IBMPlexMono-500-latin.woff2 +0 -0
  13. package/generators/app/templates/typescript/public/fonts/IBMPlexSans-400_600-latin-ext.woff2 +0 -0
  14. package/generators/app/templates/typescript/public/fonts/IBMPlexSans-400_600-latin.woff2 +0 -0
  15. package/generators/app/templates/typescript/public/fonts/InstrumentSerif-400-latin-ext.woff2 +0 -0
  16. package/generators/app/templates/typescript/public/fonts/InstrumentSerif-400-latin.woff2 +0 -0
  17. package/generators/app/templates/typescript/public/index.html +16 -0
  18. package/generators/app/templates/typescript/scripts/build.mjs +49 -0
  19. package/generators/app/templates/typescript/scripts/privacy-check.mjs +21 -0
  20. package/generators/app/templates/typescript/scripts/render-content.mjs +14 -0
  21. package/generators/app/templates/typescript/server/data-store.ts +123 -0
  22. package/generators/app/templates/typescript/server/index.ts +338 -0
  23. package/generators/app/templates/typescript/server/privacy-adapter.ts +21 -0
  24. package/generators/app/templates/typescript/src/content.ts +491 -0
  25. package/generators/app/templates/typescript/src/fonts.css +92 -0
  26. package/generators/app/templates/typescript/src/main.ts +251 -0
  27. package/generators/app/templates/typescript/src/service-agreement.ts +40 -0
  28. package/generators/app/templates/typescript/src/style.css +916 -0
  29. package/generators/app/templates/typescript/src/tokens.css +56 -0
  30. package/generators/app/templates/typescript/test/server.test.mjs +287 -0
  31. package/generators/app/templates/typescript/tsconfig.json +15 -0
  32. package/lib/app-module.cjs +64 -0
  33. package/lib/scaffold.cjs +214 -0
  34. package/package.json +15 -13
  35. package/generators/app/templates/app2018/.travis.yml +0 -35
  36. package/generators/app/templates/app2018/LICENSE +0 -21
  37. package/generators/app/templates/app2018/Procfile +0 -1
  38. package/generators/app/templates/app2018/README.md +0 -49
  39. package/generators/app/templates/app2018/config.xml +0 -91
  40. package/generators/app/templates/app2018/gulpfile.js +0 -63
  41. package/generators/app/templates/app2018/ionic.config.json +0 -9
  42. package/generators/app/templates/app2018/package.json +0 -90
  43. package/generators/app/templates/app2018/platforms/README.md +0 -1
  44. package/generators/app/templates/app2018/resources/README.md +0 -6
  45. package/generators/app/templates/app2018/resources/icon.png +0 -0
  46. package/generators/app/templates/app2018/resources/marketing-01-ipad.png +0 -0
  47. package/generators/app/templates/app2018/resources/marketing-01-iphone.png +0 -0
  48. package/generators/app/templates/app2018/resources/marketing-02-ipad.png +0 -0
  49. package/generators/app/templates/app2018/resources/marketing-02-iphone.png +0 -0
  50. package/generators/app/templates/app2018/resources/marketing-03-ipad.png +0 -0
  51. package/generators/app/templates/app2018/resources/marketing-03-iphone.png +0 -0
  52. package/generators/app/templates/app2018/resources/marketing.json +0 -19
  53. package/generators/app/templates/app2018/resources/splash.png +0 -0
  54. package/generators/app/templates/app2018/src/app/app.component.ts +0 -88
  55. package/generators/app/templates/app2018/src/app/app.html +0 -19
  56. package/generators/app/templates/app2018/src/app/app.module.ts +0 -40
  57. package/generators/app/templates/app2018/src/app/app.scss +0 -16
  58. package/generators/app/templates/app2018/src/app/main.ts +0 -5
  59. package/generators/app/templates/app2018/src/app/shared/card.model.ts +0 -10
  60. package/generators/app/templates/app2018/src/app/shared/profile.service.ts +0 -46
  61. package/generators/app/templates/app2018/src/app/shared/shared.module.ts +0 -35
  62. package/generators/app/templates/app2018/src/app/shared/storage.service.ts +0 -11
  63. package/generators/app/templates/app2018/src/app/shared/version.const.ts +0 -5
  64. package/generators/app/templates/app2018/src/assets/icon/favicon.ico +0 -0
  65. package/generators/app/templates/app2018/src/assets/imgs/gg.png +0 -0
  66. package/generators/app/templates/app2018/src/assets/imgs/github.png +0 -0
  67. package/generators/app/templates/app2018/src/assets/imgs/login.svg +0 -5668
  68. package/generators/app/templates/app2018/src/assets/imgs/logo.png +0 -0
  69. package/generators/app/templates/app2018/src/index.html +0 -49
  70. package/generators/app/templates/app2018/src/manifest.json +0 -13
  71. package/generators/app/templates/app2018/src/pages/home/home.html +0 -56
  72. package/generators/app/templates/app2018/src/pages/home/home.scss +0 -3
  73. package/generators/app/templates/app2018/src/pages/home/home.ts +0 -126
  74. package/generators/app/templates/app2018/src/pages/login/login.html +0 -56
  75. package/generators/app/templates/app2018/src/pages/login/login.scss +0 -44
  76. package/generators/app/templates/app2018/src/pages/login/login.ts +0 -59
  77. package/generators/app/templates/app2018/src/pages/profile/profile.html +0 -33
  78. package/generators/app/templates/app2018/src/pages/profile/profile.scss +0 -3
  79. package/generators/app/templates/app2018/src/pages/profile/profile.ts +0 -36
  80. package/generators/app/templates/app2018/src/service-worker.js +0 -31
  81. package/generators/app/templates/app2018/src/theme/variables.scss +0 -88
  82. package/generators/app/templates/app2018/tests/karma.unit-tests.conf.js +0 -84
  83. package/generators/app/templates/app2018/tests/unit/app.test.js +0 -10
  84. package/generators/app/templates/app2018/tsconfig.json +0 -28
  85. package/generators/app/templates/app2018/tslint.json +0 -8
  86. package/generators/app/templates/app2018/web.js +0 -21
  87. package/generators/app/templates/app2021/.travis.yml +0 -32
  88. package/generators/app/templates/app2021/LICENSE +0 -21
  89. package/generators/app/templates/app2021/Procfile +0 -1
  90. package/generators/app/templates/app2021/README.md +0 -45
  91. package/generators/app/templates/app2021/angular.json +0 -188
  92. package/generators/app/templates/app2021/browserslist +0 -12
  93. package/generators/app/templates/app2021/ionic.config.json +0 -9
  94. package/generators/app/templates/app2021/karma.conf.js +0 -31
  95. package/generators/app/templates/app2021/package-lock.json +0 -5
  96. package/generators/app/templates/app2021/package.json +0 -77
  97. package/generators/app/templates/app2021/platforms/README.md +0 -1
  98. package/generators/app/templates/app2021/resources/README.md +0 -6
  99. package/generators/app/templates/app2021/resources/icon.png +0 -0
  100. package/generators/app/templates/app2021/resources/marketing-01-ipad.png +0 -0
  101. package/generators/app/templates/app2021/resources/marketing-01-iphone.png +0 -0
  102. package/generators/app/templates/app2021/resources/marketing-02-ipad.png +0 -0
  103. package/generators/app/templates/app2021/resources/marketing-02-iphone.png +0 -0
  104. package/generators/app/templates/app2021/resources/marketing-03-ipad.png +0 -0
  105. package/generators/app/templates/app2021/resources/marketing-03-iphone.png +0 -0
  106. package/generators/app/templates/app2021/resources/marketing.json +0 -19
  107. package/generators/app/templates/app2021/resources/splash.png +0 -0
  108. package/generators/app/templates/app2021/src/app/app-routing.module.ts +0 -25
  109. package/generators/app/templates/app2021/src/app/app.component.html +0 -3
  110. package/generators/app/templates/app2021/src/app/app.component.scss +0 -0
  111. package/generators/app/templates/app2021/src/app/app.component.spec.ts +0 -50
  112. package/generators/app/templates/app2021/src/app/app.component.ts +0 -28
  113. package/generators/app/templates/app2021/src/app/app.module.ts +0 -24
  114. package/generators/app/templates/app2021/src/app/content/content-routing.module.ts +0 -16
  115. package/generators/app/templates/app2021/src/app/content/content.module.ts +0 -20
  116. package/generators/app/templates/app2021/src/app/content/content.page.html +0 -31
  117. package/generators/app/templates/app2021/src/app/content/content.page.scss +0 -0
  118. package/generators/app/templates/app2021/src/app/content/content.page.ts +0 -26
  119. package/generators/app/templates/app2021/src/app/explore-container/explore-container.component.html +0 -4
  120. package/generators/app/templates/app2021/src/app/explore-container/explore-container.component.scss +0 -4
  121. package/generators/app/templates/app2021/src/app/explore-container/explore-container.component.spec.ts +0 -24
  122. package/generators/app/templates/app2021/src/app/explore-container/explore-container.component.ts +0 -16
  123. package/generators/app/templates/app2021/src/app/explore-container/explore-container.module.ts +0 -14
  124. package/generators/app/templates/app2021/src/app/my/my-routing.module.ts +0 -36
  125. package/generators/app/templates/app2021/src/app/my/my.module.ts +0 -19
  126. package/generators/app/templates/app2021/src/app/my/my.page.html +0 -15
  127. package/generators/app/templates/app2021/src/app/my/my.page.scss +0 -1
  128. package/generators/app/templates/app2021/src/app/my/my.page.spec.ts +0 -26
  129. package/generators/app/templates/app2021/src/app/my/my.page.ts +0 -25
  130. package/generators/app/templates/app2021/src/app/profile/profile-routing.module.ts +0 -16
  131. package/generators/app/templates/app2021/src/app/profile/profile.module.ts +0 -20
  132. package/generators/app/templates/app2021/src/app/profile/profile.page.html +0 -49
  133. package/generators/app/templates/app2021/src/app/profile/profile.page.scss +0 -0
  134. package/generators/app/templates/app2021/src/app/profile/profile.page.spec.ts +0 -25
  135. package/generators/app/templates/app2021/src/app/profile/profile.page.ts +0 -44
  136. package/generators/app/templates/app2021/src/app/shared/const.ts +0 -3
  137. package/generators/app/templates/app2021/src/app/signin/signin-routing.module.ts +0 -16
  138. package/generators/app/templates/app2021/src/app/signin/signin.module.ts +0 -18
  139. package/generators/app/templates/app2021/src/app/signin/signin.page.html +0 -53
  140. package/generators/app/templates/app2021/src/app/signin/signin.page.scss +0 -36
  141. package/generators/app/templates/app2021/src/app/signin/signin.page.ts +0 -69
  142. package/generators/app/templates/app2021/src/assets/icon/favicon.png +0 -0
  143. package/generators/app/templates/app2021/src/assets/shapes.svg +0 -1
  144. package/generators/app/templates/app2021/src/environments/environment.prod.ts +0 -3
  145. package/generators/app/templates/app2021/src/environments/environment.ts +0 -16
  146. package/generators/app/templates/app2021/src/global.scss +0 -26
  147. package/generators/app/templates/app2021/src/index.html +0 -26
  148. package/generators/app/templates/app2021/src/main.ts +0 -12
  149. package/generators/app/templates/app2021/src/polyfills.ts +0 -67
  150. package/generators/app/templates/app2021/src/test.ts +0 -20
  151. package/generators/app/templates/app2021/src/theme/variables.scss +0 -232
  152. package/generators/app/templates/app2021/src/zone-flags.ts +0 -5
  153. package/generators/app/templates/app2021/tests/protractor.conf.js +0 -36
  154. package/generators/app/templates/app2021/tests/src/app.e2e-spec.ts +0 -14
  155. package/generators/app/templates/app2021/tests/src/app.po.ts +0 -11
  156. package/generators/app/templates/app2021/tests/tsconfig.json +0 -13
  157. package/generators/app/templates/app2021/tsconfig.app.json +0 -18
  158. package/generators/app/templates/app2021/tsconfig.json +0 -23
  159. package/generators/app/templates/app2021/tsconfig.spec.json +0 -18
  160. package/generators/app/templates/app2021/tslint.json +0 -149
  161. 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
- [![NPM version][npm-image]][npm-url]
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
- > build great mobile and web angular/ionic apps based on fidj.ovh authentication.
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
- ## Prerequisites
7
+ ## Content app — static hosting
11
8
 
12
- Look at mat's gist : https://gist.github.com/mlefree/2156f66dfb441f107bef157dde56a836
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
- ## Installation
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
- First, install [Yeoman](http://yeoman.io) and generator-fidj using [npm](https://www.npmjs.com/) (we assume you have pre-installed [node.js](https://nodejs.org/)) :
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
- ## Development
19
+ ## Backend example — protected actions
35
20
 
36
- Make sure you've got yeoman installed :
37
- ```bash
38
- npm install -g yo
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
- Clone the repo, install it :
44
- ```bash
45
- cd generator-fidj
46
- npm install
47
- npm link
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
- And then use your local Fidj generator :
51
- ```bash
52
- mkdir your-app && cd your-app && \
53
- yo @ofidj/fidj my-app app2021 your-app-id "" me-as-user "my description" \
54
- "Welcome there" \
55
- "Hey<br>Heres my content: <img src=blank /><br> \o/ "
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
- # look at the code : code .
58
- # or start it : npm start
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
- ## License
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
- MIT 2021 © [fidj.ovh](fidj.ovh)
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
- [npm-image]: https://badge.fury.io/js/@ofidj%2Fgenerator-fidj.svg
66
- [npm-url]: https://npmjs.org/package/@ofidj/generator-fidj
67
- [travis-image]: https://travis-ci.org/ofidj/generator-fidj.svg?branch=master
68
- [travis-url]: https://travis-ci.org/ofidj/generator-fidj
69
- [daviddm-image]: https://david-dm.org/ofidj/generator-fidj.svg?theme=shields.io
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
+ }
@@ -1,200 +1,111 @@
1
- const Generator = require('yeoman-generator');
2
- const yosay = require('yosay');
3
- const chalk = require('chalk');
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.hasBeenRecordedInFidjOvh = true;
10
- this.templateName = 'app2021'; //'app2021'; //'app2018' ''ionic2020'
11
-
12
- this.option('skip-welcome-message', {
13
- desc: 'Skips the welcome message',
14
- type: Boolean
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
- if (!this.options['skip-welcome-message'] && !this.options.appId) {
33
- this.log(yosay('\'Allo \'Welcome to ' + chalk.red('fidj.ovh') + ' generator'));
34
-
35
- const promptConditions = [{
36
- type: 'confirm',
37
- name: 'hasBeenRecordedInFidjOvh',
38
- message: 'Did you recorded your app in fidj.ovh and received your appId ?',
39
- default: false
40
- }];
41
-
42
- const conditions = await this.prompt(promptConditions);
43
- this.hasBeenRecordedInFidjOvh = !!conditions.hasBeenRecordedInFidjOvh;
44
- if (!this.hasBeenRecordedInFidjOvh) {
45
- const err = 'You need to submit your app on https://fidj.ovh first. ' +
46
- 'It will help you to build a valid app name and give you a appId.';
47
- this.log(yosay('Sorry : ' + chalk.red(err) + ' See you in 2 minutes ?'));
48
- }
49
- }
50
-
51
- if (!this.options.appname || !this.options.appUserName || !this.options.appDescription) {
52
- const prompts = [
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
- async writing() {
118
- if (!this.hasBeenRecordedInFidjOvh) {
119
- return;
120
- }
121
-
122
- let copyAll = true;
123
- if (this.options.appType === 'app2018') {
124
- this.templateName = 'app2018';
125
- } else {
126
- this.templateName = 'app2021';
127
- }
128
-
129
- const now = new Date();
130
- const version = '' + ('' + now.getFullYear()).substr(2) + '.' + (now.getMonth() + 1) + '.' + now.getDate();
131
-
132
- const appArguments = {
133
- appName: this.props.appName,
134
- appNameStrict: this.props.appName.replace(/[^\w\s]/gi, '').replace(/\s+/g, '').replace(/[\. ,:-]+/g, '').toLowerCase(),
135
- appDescription: this.props.appDescription,
136
- appWelcome: this.props.appWelcome,
137
- appContent: this.props.appContent,
138
- appUserName: this.props.appUserName,
139
- appHomepage: this.props02.appHomepage,
140
- appPackage: this.props02.appPackage,
141
- appId: this.props02.appId,
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;