@vevedh/create-feathers-app 0.0.0-stage → 0.1.0-rc.4
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/LICENSE +21 -0
- package/README.md +79 -2
- package/bin/create-feathers-app.mjs +44 -0
- package/lib/planner/options.mjs +74 -0
- package/lib/planner/plan.mjs +107 -0
- package/lib/scaffold.mjs +300 -0
- package/lib/services.mjs +110 -0
- package/package.json +32 -4
- package/templates/common/dev-all-quasar-ssr.mjs +37 -0
- package/templates/common/dev-all.mjs +46 -0
- package/templates/daisyui-ssg/.env.example +6 -0
- package/templates/daisyui-ssg/README.md +21 -0
- package/templates/daisyui-ssg/api/server.mjs +45 -0
- package/templates/daisyui-ssg/api/services/health/health.class.mjs +3 -0
- package/templates/daisyui-ssg/api/services/health/health.service.mjs +4 -0
- package/templates/daisyui-ssg/api/services/notes/notes.class.mjs +25 -0
- package/templates/daisyui-ssg/api/services/notes/notes.service.mjs +10 -0
- package/templates/daisyui-ssg/gitignore.template +36 -0
- package/templates/daisyui-ssg/index.html +2 -0
- package/templates/daisyui-ssg/package.json +39 -0
- package/templates/daisyui-ssg/scripts/prerender.mjs +14 -0
- package/templates/daisyui-ssg/scripts/serve-static.mjs +32 -0
- package/templates/daisyui-ssg/src/App.vue +14 -0
- package/templates/daisyui-ssg/src/components/NotesPanel.vue +22 -0
- package/templates/daisyui-ssg/src/entry-client.ts +9 -0
- package/templates/daisyui-ssg/src/entry-server.ts +9 -0
- package/templates/daisyui-ssg/src/stores/notes.ts +46 -0
- package/templates/daisyui-ssg/src/style.css +5 -0
- package/templates/daisyui-ssg/src/vite-env.d.ts +1 -0
- package/templates/daisyui-ssg/tsconfig.json +4 -0
- package/templates/daisyui-ssg/vite.config.ts +6 -0
- package/templates/daisyui-ssr/.env.example +6 -0
- package/templates/daisyui-ssr/README.md +21 -0
- package/templates/daisyui-ssr/api/server.mjs +45 -0
- package/templates/daisyui-ssr/api/services/health/health.class.mjs +3 -0
- package/templates/daisyui-ssr/api/services/health/health.service.mjs +4 -0
- package/templates/daisyui-ssr/api/services/notes/notes.class.mjs +25 -0
- package/templates/daisyui-ssr/api/services/notes/notes.service.mjs +10 -0
- package/templates/daisyui-ssr/gitignore.template +36 -0
- package/templates/daisyui-ssr/index.html +2 -0
- package/templates/daisyui-ssr/package.json +39 -0
- package/templates/daisyui-ssr/scripts/serve-ssr.mjs +47 -0
- package/templates/daisyui-ssr/src/App.vue +14 -0
- package/templates/daisyui-ssr/src/components/NotesPanel.vue +22 -0
- package/templates/daisyui-ssr/src/entry-client.ts +9 -0
- package/templates/daisyui-ssr/src/entry-server.ts +9 -0
- package/templates/daisyui-ssr/src/stores/notes.ts +46 -0
- package/templates/daisyui-ssr/src/style.css +5 -0
- package/templates/daisyui-ssr/src/vite-env.d.ts +1 -0
- package/templates/daisyui-ssr/tsconfig.json +4 -0
- package/templates/daisyui-ssr/vite.config.ts +7 -0
- package/templates/quasar-ssr/.env.example +11 -0
- package/templates/quasar-ssr/api/server.mjs +45 -0
- package/templates/quasar-ssr/api/services/health/health.class.mjs +3 -0
- package/templates/quasar-ssr/api/services/health/health.service.mjs +4 -0
- package/templates/quasar-ssr/api/services/notes/notes.class.mjs +25 -0
- package/templates/quasar-ssr/api/services/notes/notes.service.mjs +10 -0
- package/templates/quasar-ssr/gitignore.template +36 -0
- package/templates/quasar-ssr/index.html +12 -0
- package/templates/quasar-ssr/quasar.config.ts +27 -0
- package/templates/quasar-ssr/scripts/edge-proxy.mjs +30 -0
- package/templates/quasar-ssr/scripts/edge-routing.mjs +14 -0
- package/templates/quasar-ssr/src/App.vue +1 -0
- package/templates/quasar-ssr/src/css/app.css +20 -0
- package/templates/quasar-ssr/src/pages/IndexPage.vue +146 -0
- package/templates/quasar-ssr/src/pages/NotFoundPage.vue +11 -0
- package/templates/quasar-ssr/src/router/index.ts +8 -0
- package/templates/quasar-ssr/src/router/routes.ts +6 -0
- package/templates/quasar-ssr/src/stores/index.ts +4 -0
- package/templates/quasar-ssr/src/stores/notes.ts +73 -0
- package/templates/quasar-ssr/src-ssr/middlewares/render.ts +15 -0
- package/templates/quasar-ssr/src-ssr/server.ts +42 -0
- package/templates/quasar-ssr/tsconfig.json +4 -0
- package/templates/spa/common/.env.example +8 -0
- package/templates/spa/common/api/server.mjs +46 -0
- package/templates/spa/common/api/services/health/health.class.mjs +3 -0
- package/templates/spa/common/api/services/health/health.service.mjs +4 -0
- package/templates/spa/common/api/services/notes/notes.class.mjs +25 -0
- package/templates/spa/common/api/services/notes/notes.service.mjs +10 -0
- package/templates/spa/common/gitignore.template +39 -0
- package/templates/spa/common/index.html +3 -0
- package/templates/spa/common/src/components/NotesPanel.vue +25 -0
- package/templates/spa/common/src/stores/notes.ts +105 -0
- package/templates/spa/common/src/vite-env.d.ts +1 -0
- package/templates/spa/common/tsconfig.json +4 -0
- package/templates/spa/daisyui/src/App.vue +2 -0
- package/templates/spa/daisyui/src/components/NotesPanel.vue +39 -0
- package/templates/spa/daisyui/src/main.ts +5 -0
- package/templates/spa/daisyui/src/style.css +5 -0
- package/templates/spa/daisyui/vite.config.ts +8 -0
- package/templates/spa/quasar2/src/App.vue +3 -0
- package/templates/spa/quasar2/src/components/NotesPanel.vue +41 -0
- package/templates/spa/quasar2/src/main.ts +7 -0
- package/templates/spa/quasar2/src/style.css +1 -0
- package/templates/spa/quasar2/vite.config.ts +8 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 vite-plugin-feathersjs contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,80 @@
|
|
|
1
|
-
#
|
|
1
|
+
# create-feathers-app
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Générez des applications FeathersJS v5 avec Vue 3, Pinia, Vite, Quasar 2 ou Tailwind CSS + daisyUI.
|
|
4
|
+
|
|
5
|
+
Le CLI est indépendant du runtime [`@vevedh/vite-plugin-feathersjs`](https://www.npmjs.com/package/@vevedh/vite-plugin-feathersjs) (version 0.2.6). La version candidate `0.1.0-rc.4` **n'est pas encore publiée**. Elle ajoute la PWA optionnelle au rendu SPA.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```powershell
|
|
10
|
+
# Préversion disponible sur npm : 0.1.0-rc.1
|
|
11
|
+
npm exec --yes --package=@vevedh/create-feathers-app@0.1.0-rc.1 -- create-feathers-app portail --ui daisyui --render spa
|
|
12
|
+
|
|
13
|
+
# R17-r3 candidat, depuis les sources mises en stage :
|
|
14
|
+
node .release/create-feathers-app/bin/create-feathers-app.mjs portail-ssr --ui quasar2 --render ssr
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
| Interface | SPA | SSR | SSG |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| Quasar 2 | Générable | Générable, validation Windows en cours | Bloqué |
|
|
20
|
+
| daisyUI 5 | Générable | Générable | Générable |
|
|
21
|
+
|
|
22
|
+
## PWA optionnelle (SPA uniquement)
|
|
23
|
+
|
|
24
|
+
Le paramètre `--pwa on` ajoute `vite-plugin-pwa` v2 en dépendance de développement, une icône SVG locale de démonstration et un manifeste Web App. Le service worker, désactivé en développement, est généré au build et conserve uniquement des ressources statiques, jamais les réponses `/api` ou les échanges Socket.IO. Par défaut, `--pwa off` ne change pas les templates existants.
|
|
25
|
+
|
|
26
|
+
```powershell
|
|
27
|
+
node .release/create-feathers-app/bin/create-feathers-app.mjs portail-spa --ui quasar2 --render spa --pwa on --dry-run
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
SSR/SSG avec PWA est refusé tant que leur cycle de vie n'est pas certifié. Pour une installation sur les navigateurs ciblés, remplacer l'icône SVG de démonstration par des icônes PNG 192 et 512 pixels, puis contrôler HTTPS, les mises à jour et le mode hors-ligne.
|
|
31
|
+
|
|
32
|
+
## Ajouter un service Feathers
|
|
33
|
+
|
|
34
|
+
```powershell
|
|
35
|
+
cd portail
|
|
36
|
+
node ../chemin/vers/create-feathers-app/bin/create-feathers-app.mjs service add tickets --schema zod --db main
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Le générateur ajoute le module `api/services/tickets` et son inscription dans le registre Feathers. Les schémas disponibles sont `typebox`, `json` et `zod`. Le service mémoire est destiné au laboratoire local, avec CRUD REST et validation des données.
|
|
40
|
+
|
|
41
|
+
## Profils d'identité et de bases
|
|
42
|
+
|
|
43
|
+
La création de projet accepte `--auth none|jwt|oauth2|oidc|keycloak`, `--schema typebox|json|zod` et `--db main:memory,audit:mongodb` avec alias nommés. **Les authentifications externes et les connexions persistantes sont des profils de configuration, pas des intégrations opérationnelles.** L'API échoue volontairement au démarrage jusqu'au raccordement et à la certification du fournisseur ou du SGBD.
|
|
44
|
+
|
|
45
|
+
Les projets sans authentification sont exclusivement des démonstrations locales. Ils ne doivent jamais être exposés tels quels sur Internet. Aucun secret, mot de passe ou jeton n'est inclus dans les templates.
|
|
46
|
+
|
|
47
|
+
## Windows
|
|
48
|
+
|
|
49
|
+
```powershell
|
|
50
|
+
corepack pnpm install
|
|
51
|
+
corepack pnpm run typecheck
|
|
52
|
+
corepack pnpm run build
|
|
53
|
+
corepack pnpm run dev:all
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
SPA et daisyUI SSR/SSG utilisent `http://127.0.0.1:5173`. Quasar SSR utilise `http://127.0.0.1:8080` via un proxy local ; le SSR brut écoute sur 9100.
|
|
57
|
+
|
|
58
|
+
La documentation publique VitePress figure dans `docs/cli` du dépôt et sera déployée sur GitHub Pages après activation du workflow de publication.
|
|
59
|
+
|
|
60
|
+
## API Feathers intégrée à Vite en développement (RC4)
|
|
61
|
+
|
|
62
|
+
Pour les applications SPA, `--dev-api vite` est désormais la valeur par défaut.
|
|
63
|
+
Le plugin public `@vevedh/vite-plugin-feathersjs@0.2.6` est chargé depuis
|
|
64
|
+
`vite.config.ts` et monte les services sur `/api` pendant `vite dev`.
|
|
65
|
+
Il n'y a pas de second serveur REST ni de proxy REST à démarrer.
|
|
66
|
+
|
|
67
|
+
L'option `--dev-api separate` conserve l'ancien comportement à deux processus.
|
|
68
|
+
Pour SSR/SSG, le mode séparé reste obligatoire. Avec `--transport both`,
|
|
69
|
+
Socket.IO reste sur le port privé `127.0.0.1:3031`, accessible depuis la SPA
|
|
70
|
+
via le proxy WebSocket Vite. La configuration de Feathers échoue au chargement
|
|
71
|
+
si les profils d'identité ou bases externes ne sont pas opérationnels.
|
|
72
|
+
|
|
73
|
+
```powershell
|
|
74
|
+
node .release/create-feathers-app/bin/create-feathers-app.mjs mon-spa --ui quasar2 --render spa --dev-api vite --transport both
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Production** : le plugin Vite s'exécute uniquement en développement.
|
|
78
|
+
Le serveur `api/server.mjs` autonome reste disponible et obligatoire pour
|
|
79
|
+
le déploiement ; `vite preview` n'héberge pas l'API.
|
|
80
|
+
Le runtime `0.2.6` n'a pas été modifié.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { dirname,resolve } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { readFile } from 'node:fs/promises'
|
|
5
|
+
import { createGenerator } from '../lib/scaffold.mjs'
|
|
6
|
+
import { runServiceCommand } from '../lib/services.mjs'
|
|
7
|
+
const packageRoot=resolve(dirname(fileURLToPath(import.meta.url)),'..')
|
|
8
|
+
const {version}=JSON.parse(await readFile(resolve(packageRoot,'package.json'),'utf8'))
|
|
9
|
+
const args=process.argv.slice(2)
|
|
10
|
+
if(args.includes('--help')||args.includes('-h')||!args.length){
|
|
11
|
+
console.log(`create-feathers-app ${version}
|
|
12
|
+
|
|
13
|
+
Usage: create-feathers-app <name> [options]
|
|
14
|
+
--name <name> Project name (alternative to positional name)
|
|
15
|
+
--dir <directory> Safe folder name (within current directory)
|
|
16
|
+
--ui <quasar2|daisyui> Default daisyui
|
|
17
|
+
--render <spa|ssr|ssg> Default spa
|
|
18
|
+
--adapter <koa|express> Default koa
|
|
19
|
+
--transport <rest|both> Default rest (both only for SPA)
|
|
20
|
+
--pwa <on|off> Optional offline SPA shell (default off, no API caching)
|
|
21
|
+
--dev-api <vite|separate> SPA default vite; SSR/SSG always separate
|
|
22
|
+
--dry-run Preview files without writing
|
|
23
|
+
--json Machine-readable output
|
|
24
|
+
--schema <typebox|json|zod> Service schema preset (default typebox)
|
|
25
|
+
--auth <none|jwt|oauth2|oidc|keycloak> Identity configuration blueprint
|
|
26
|
+
--db <alias:adapter[,alias:adapter]> Named connections (default main:memory)
|
|
27
|
+
--version Print CLI version
|
|
28
|
+
|
|
29
|
+
Service: create-feathers-app service add <name> [--project <path>] [--schema typebox|json|zod] [--db <alias>] [--auth none|jwt] [--dry-run]
|
|
30
|
+
|
|
31
|
+
Available: Quasar 2 SPA/SSR; daisyUI SPA, SSR, SSG.
|
|
32
|
+
Quasar SSR uses the R16-certified architecture; R17-generated SSR requires its own Windows E2E certification. Quasar SSG not supported.`)
|
|
33
|
+
}else if(args.includes('--version')||args.includes('-v'))console.log(version)
|
|
34
|
+
else if(args[0]==='service') {
|
|
35
|
+
try { await runServiceCommand(args.slice(1),process.cwd()) } catch(error){console.error(`FAIL: ${error.message}`);process.exitCode=1}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const {parse,generate}=createGenerator({outputRoot:process.cwd(),templatesRoot:resolve(packageRoot,'templates'),reportRoot:process.cwd()})
|
|
39
|
+
try {
|
|
40
|
+
const options=parse(args)
|
|
41
|
+
const result=await generate(options)
|
|
42
|
+
console.log(options.json?JSON.stringify(result,null,2):`${options.dryRun?'DRY RUN':'CREATED'}: ${result.destination} (${result.files.length} files)`)
|
|
43
|
+
} catch(error){console.error(`FAIL: ${error.message}`);process.exitCode=1}
|
|
44
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** Candidate options for a future scaffolding CLI. No files or runtime services
|
|
2
|
+
* are generated by this read-only planner. Keep mappings reviewable and explicit.
|
|
3
|
+
*/
|
|
4
|
+
export const UI_PRESETS = Object.freeze(['quasar2', 'daisyui', 'daisyui-kit'])
|
|
5
|
+
export const SCHEMA_PRESETS = Object.freeze(['typebox', 'json', 'zod'])
|
|
6
|
+
export const AUTH_PRESETS = Object.freeze(['none', 'jwt', 'local', 'oauth', 'oidc', 'ldap'])
|
|
7
|
+
export const DATABASE_PRESETS = Object.freeze(['memory', 'mongodb', 'postgresql', 'mariadb', 'mysql', 'sqlite'])
|
|
8
|
+
export const RENDER_PRESETS = Object.freeze(['spa', 'ssr', 'ssg'])
|
|
9
|
+
export const RENDER_ENGINES = Object.freeze(['auto', 'vite', 'quasar-cli-vite'])
|
|
10
|
+
export const TRANSPORT_PRESETS = Object.freeze(['rest', 'socketio', 'both'])
|
|
11
|
+
export const NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/
|
|
12
|
+
|
|
13
|
+
const AVAILABLE_OPTIONS = new Set(['--name', '--ui', '--render', '--pwa', '--schema', '--auth', '--db', '--primary', '--transport', '--engine', '--dry-run', '--json', '--help'])
|
|
14
|
+
|
|
15
|
+
function requiredChoice(value, choices, name) {
|
|
16
|
+
if (!choices.includes(value)) throw new Error(`Invalid ${name}: expected ${choices.join('|')}`)
|
|
17
|
+
return value
|
|
18
|
+
}
|
|
19
|
+
function parseUniqueCsv(value, name) {
|
|
20
|
+
if (typeof value !== 'string' || !value || value.length > 512) throw new Error(`Invalid ${name}`)
|
|
21
|
+
const parts = value.split(',')
|
|
22
|
+
if (!parts.length || parts.some(part => !part || part !== part.trim()) || new Set(parts).size !== parts.length) throw new Error(`Invalid or duplicate ${name}`)
|
|
23
|
+
return parts
|
|
24
|
+
}
|
|
25
|
+
export function parsePlanArguments(args) {
|
|
26
|
+
if (args.includes('--help') || args.includes('-h')) return { help: true }
|
|
27
|
+
const options = { name: 'my-feathers-app', ui: 'daisyui', render: 'spa', pwa: 'off', schema: 'typebox', auth: 'none', db: 'main:memory', primary: 'main', transport: 'rest', engine: 'auto', dryRun: true, json: false }
|
|
28
|
+
const seen = new Set()
|
|
29
|
+
for (let index = 0; index < args.length; index++) {
|
|
30
|
+
const flag = args[index]
|
|
31
|
+
if (!AVAILABLE_OPTIONS.has(flag) || flag === '--help' || seen.has(flag)) throw new Error(`Unknown or duplicate option: ${flag}`)
|
|
32
|
+
seen.add(flag)
|
|
33
|
+
if (flag === '--dry-run' || flag === '--json') options[flag === '--dry-run' ? 'dryRun' : 'json'] = true
|
|
34
|
+
else {
|
|
35
|
+
const value = args[++index]
|
|
36
|
+
if (!value || value.startsWith('--')) throw new Error(`Missing value for ${flag}`)
|
|
37
|
+
options[flag.slice(2)] = value
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return options
|
|
41
|
+
}
|
|
42
|
+
export function normalizePlanOptions(input) {
|
|
43
|
+
const name = input.name ?? 'my-feathers-app'
|
|
44
|
+
if (typeof name !== 'string' || name.length > 48 || !NAME_PATTERN.test(name)) throw new Error('Invalid --name: lowercase kebab-case, maximum 48 characters')
|
|
45
|
+
const ui = requiredChoice(input.ui ?? 'daisyui', UI_PRESETS, '--ui')
|
|
46
|
+
const render = requiredChoice(input.render ?? 'spa', RENDER_PRESETS, '--render')
|
|
47
|
+
const pwa = requiredChoice(input.pwa ?? 'off', ['on', 'off'], '--pwa')
|
|
48
|
+
const schema = requiredChoice(input.schema ?? 'typebox', SCHEMA_PRESETS, '--schema')
|
|
49
|
+
const transport = requiredChoice(input.transport ?? 'rest', TRANSPORT_PRESETS, '--transport')
|
|
50
|
+
const requestedEngine = requiredChoice(input.engine ?? 'auto', RENDER_ENGINES, '--engine')
|
|
51
|
+
// Quasar Vite plugin works in SPA; Quasar CLI with Vite is a separate toolchain
|
|
52
|
+
// required for SSR/SSG. Neither integrates this Feathers plugin automatically.
|
|
53
|
+
const engine = requestedEngine === 'auto'
|
|
54
|
+
? (ui === 'quasar2' && render !== 'spa' ? 'quasar-cli-vite' : 'vite')
|
|
55
|
+
: requestedEngine
|
|
56
|
+
if (ui !== 'quasar2' && engine === 'quasar-cli-vite') throw new Error('Quasar CLI is only compatible with --ui quasar2')
|
|
57
|
+
if (ui === 'quasar2' && render !== 'spa' && engine === 'vite') throw new Error('Quasar standalone Vite plugin supports SPA only; select --engine quasar-cli-vite for SSR/SSG')
|
|
58
|
+
const auth = parseUniqueCsv(input.auth ?? 'none', '--auth')
|
|
59
|
+
for (const strategy of auth) requiredChoice(strategy, AUTH_PRESETS, '--auth')
|
|
60
|
+
if (auth.includes('none') && auth.length > 1) throw new Error('Cannot combine --auth none with authentication strategies')
|
|
61
|
+
// AuthenticationService issues JWTs for the standard local and OAuth flows.
|
|
62
|
+
if (auth.some(strategy => ['local', 'oauth'].includes(strategy)) && !auth.includes('jwt')) auth.unshift('jwt')
|
|
63
|
+
const databases = parseUniqueCsv(input.db ?? 'main:memory', '--db').map(spec => {
|
|
64
|
+
const match = /^([a-z][a-z0-9-]{0,31}):([a-z]+)$/.exec(spec)
|
|
65
|
+
if (!match) throw new Error('Invalid --db: use alias:adapter[,alias:adapter]')
|
|
66
|
+
const [, alias, adapter] = match
|
|
67
|
+
requiredChoice(adapter, DATABASE_PRESETS, '--db adapter')
|
|
68
|
+
return { alias, adapter }
|
|
69
|
+
})
|
|
70
|
+
if (new Set(databases.map(db => db.alias)).size !== databases.length) throw new Error('Duplicate database connection alias')
|
|
71
|
+
const primary = input.primary ?? 'main'
|
|
72
|
+
if (!databases.some(db => db.alias === primary)) throw new Error('--primary must match a --db alias')
|
|
73
|
+
return { name, ui, render, engine, pwa, schema, transport, auth, databases, primary, dryRun: true, json: Boolean(input.json) }
|
|
74
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { normalizePlanOptions } from './options.mjs'
|
|
2
|
+
|
|
3
|
+
const DATABASE_PACKAGES = {
|
|
4
|
+
memory: ['@feathersjs/memory'],
|
|
5
|
+
mongodb: ['@feathersjs/mongodb', 'mongodb'],
|
|
6
|
+
postgresql: ['@feathersjs/knex', 'knex', 'pg'],
|
|
7
|
+
mariadb: ['@feathersjs/knex', 'knex', 'mysql2'],
|
|
8
|
+
mysql: ['@feathersjs/knex', 'knex', 'mysql2'],
|
|
9
|
+
sqlite: ['@feathersjs/knex', 'knex'],
|
|
10
|
+
}
|
|
11
|
+
const PROVIDERS = {
|
|
12
|
+
jwt: ['@feathersjs/authentication'],
|
|
13
|
+
local: ['@feathersjs/authentication-local'],
|
|
14
|
+
oauth: ['@feathersjs/authentication-oauth'],
|
|
15
|
+
oidc: [],
|
|
16
|
+
ldap: [],
|
|
17
|
+
}
|
|
18
|
+
/** Deterministic, read-only plan. This is a capability contract, NOT a scaffold.
|
|
19
|
+
* Store no connection URLs or authentication secrets in this plan.
|
|
20
|
+
*/
|
|
21
|
+
export function buildProjectPlan(rawOptions) {
|
|
22
|
+
const options = normalizePlanOptions(rawOptions)
|
|
23
|
+
const packages = new Set(['vite', 'vue', 'pinia', '@feathersjs/feathers', '@feathersjs/koa', '@vevedh/vite-plugin-feathersjs'])
|
|
24
|
+
const tasks = []
|
|
25
|
+
const warnings = []
|
|
26
|
+
if (options.ui === 'quasar2') {
|
|
27
|
+
if (options.engine === 'vite') {
|
|
28
|
+
for (const pkg of ['quasar', '@quasar/extras', '@quasar/vite-plugin@^1', 'sass-embedded']) packages.add(pkg)
|
|
29
|
+
tasks.push('Vite 7 requires @quasar/vite-plugin 1.x, which supports SPA only; certify Quasar 2 component compatibility.')
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
for (const pkg of ['quasar', '@quasar/extras', '@quasar/app-vite']) packages.add(pkg)
|
|
33
|
+
tasks.push('Use Quasar CLI with Vite rather than @quasar/vite-plugin. Scaffold independently and certify integration with Feathers v5, services, Pinia, HMR and deployment.')
|
|
34
|
+
if (options.render === 'ssg') warnings.push('Quasar CLI Vite SSG is in beta: APIs and deployment requirements may change.')
|
|
35
|
+
warnings.push('Quasar CLI SSR/SSG + vite-plugin-feathersjs integration is NOT yet implemented or certified in this project.')
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
for (const pkg of ['tailwindcss', '@tailwindcss/vite', 'daisyui']) packages.add(pkg)
|
|
40
|
+
if (options.ui === 'daisyui-kit') warnings.push('daisyUI kit is an external UI-template/preset selection; its license and assets are NOT bundled or installed by the planner.')
|
|
41
|
+
tasks.push('Use the Tailwind 4 Vite plugin and daisyUI CSS; avoid duplicate global UnoCSS/Tailwind resets.')
|
|
42
|
+
if (options.render === 'ssr') {
|
|
43
|
+
packages.add('@vue/server-renderer')
|
|
44
|
+
tasks.push('Build a Vite/Vue SSR server entry, isolated per-request Vue/Pinia state, HTML template and hydration, and certify dev and production SSR.')
|
|
45
|
+
}
|
|
46
|
+
if (options.render === 'ssg') {
|
|
47
|
+
packages.add('vite-ssg')
|
|
48
|
+
tasks.push('Prerender only public/static routes; verify generated HTML and client hydration, and use a separate Feathers API for dynamic data.')
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (options.render === 'ssr') {
|
|
52
|
+
warnings.push('SSR must isolate Pinia and authentication state per request; never serialize tokens, secrets or other users’ data into HTML.')
|
|
53
|
+
tasks.push('Define a production SSR server lifecycle, security headers, cache policy and explicit separation from Feathers REST/Socket.IO endpoints.')
|
|
54
|
+
}
|
|
55
|
+
if (options.render === 'ssg') {
|
|
56
|
+
warnings.push('SSG must not bake user-specific, authenticated or private content into static HTML; dynamic services require a running Feathers API.')
|
|
57
|
+
}
|
|
58
|
+
if (options.pwa === 'on') {
|
|
59
|
+
if (options.engine === 'vite') packages.add('vite-plugin-pwa')
|
|
60
|
+
tasks.push(options.engine === 'quasar-cli-vite' ? 'Configure Quasar CLI PWA takeover / offline mode for the selected SSR/SSG build, and certify separately.' : 'Configure manifest, icons, service worker lifecycle and offline caching; test update/recovery and HTTPS deployment.')
|
|
61
|
+
}
|
|
62
|
+
if (options.schema === 'typebox') {
|
|
63
|
+
packages.add('@feathersjs/typebox')
|
|
64
|
+
packages.add('@feathersjs/schema')
|
|
65
|
+
}
|
|
66
|
+
else if (options.schema === 'json') {
|
|
67
|
+
packages.add('@feathersjs/schema')
|
|
68
|
+
tasks.push('Generate plain JSON Schema and typed Ajv validators/resolvers; compile schema once, do not bypass query validation.')
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
packages.add('zod')
|
|
72
|
+
warnings.push('Zod requires a Feathers v5 validation/resolver integration. The native TypeBox/Ajv generator cannot be reused unchanged.')
|
|
73
|
+
tasks.push('Implement and test dedicated Zod validation hooks for data, patch and query before enabling generated services.')
|
|
74
|
+
}
|
|
75
|
+
for (const strategy of options.auth.filter(item => item !== 'none')) {
|
|
76
|
+
for (const pkg of PROVIDERS[strategy]) packages.add(pkg)
|
|
77
|
+
if (strategy === 'oidc' || strategy === 'ldap') warnings.push(`${strategy.toUpperCase()} requires a separately implemented/audited authentication strategy and identity-provider configuration.`)
|
|
78
|
+
}
|
|
79
|
+
if (!options.auth.includes('none')) {
|
|
80
|
+
packages.add('@feathersjs/authentication-client')
|
|
81
|
+
tasks.push('Register AuthenticationService and per-service authenticate/authorize hooks; require RBAC for protected services.')
|
|
82
|
+
}
|
|
83
|
+
if (options.auth.includes('oauth')) tasks.push('Choose OAuth providers explicitly and configure redirect allowlists, callback HTTPS URLs, account linking and secret storage.')
|
|
84
|
+
for (const db of options.databases) for (const pkg of DATABASE_PACKAGES[db.adapter]) packages.add(pkg)
|
|
85
|
+
if (options.databases.some(db => db.adapter === 'sqlite')) warnings.push('Choose and certify a compatible SQLite driver for the target Node/Knex versions before implementation.')
|
|
86
|
+
if (options.databases.some(db => db.adapter === 'memory')) warnings.push('Memory adapter is for development/test: not durable production storage.')
|
|
87
|
+
if (options.databases.length > 1) tasks.push('Create separate named clients and bind each service to exactly one connection; cross-database transactions are not automatic.')
|
|
88
|
+
const connections = options.databases.map(({ alias, adapter }) => ({ alias, adapter, envKey: `FEATHERS_DB_${alias.replaceAll('-', '_').toUpperCase()}_URL`, isPrimary: alias === options.primary }))
|
|
89
|
+
const rendering = {
|
|
90
|
+
mode: options.render,
|
|
91
|
+
engine: options.engine,
|
|
92
|
+
deployment: options.render === 'ssr' ? 'node-ssr-runtime-required' : options.render === 'ssg' ? 'static-pages-plus-separate-feathers-api' : 'static-spa-plus-feathers-api',
|
|
93
|
+
hydrationRequired: options.render !== 'spa',
|
|
94
|
+
integrationStatus: 'not-generated-or-certified',
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
version: 2, status: 'planning-only', writesFiles: false,
|
|
98
|
+
project: options.name,
|
|
99
|
+
stack: { vite: options.engine === 'vite' ? '7' : 'managed-by-quasar-cli', vue: '3', feathers: '5', pinia: true, ui: options.ui, render: options.render, renderEngine: options.engine, pwa: options.pwa === 'on', schema: options.schema, transport: options.transport },
|
|
100
|
+
rendering,
|
|
101
|
+
authentication: { strategies: options.auth, customIntegrationRequired: options.auth.filter(item => ['oidc', 'ldap'].includes(item)) },
|
|
102
|
+
database: { primary: options.primary, connections },
|
|
103
|
+
proposedPackages: [...packages].sort(),
|
|
104
|
+
implementationTasks: tasks,
|
|
105
|
+
warnings,
|
|
106
|
+
}
|
|
107
|
+
}
|