nuxt-feathers-zod 6.4.13 → 6.4.40
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/CHANGELOG.md +87 -0
- package/README.md +161 -7
- package/dist/cli/index.mjs +26 -5
- package/dist/cli/package.json +3 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +150 -32
- package/dist/runtime/composables/useAuth.d.ts +6 -0
- package/dist/runtime/composables/useAuth.js +35 -9
- package/dist/runtime/options/database/mongodb.d.ts +58 -2
- package/dist/runtime/options/database/mongodb.js +69 -2
- package/dist/runtime/plugins/feathers-auth.js +7 -1
- package/dist/runtime/plugins/keycloak-sso.js +84 -5
- package/dist/runtime/stores/auth.d.ts +19 -1
- package/dist/runtime/stores/auth.js +16 -14
- package/dist/runtime/templates/client/plugin.js +65 -12
- package/dist/runtime/templates/server/mongodb.js +72 -37
- package/dist/runtime/utils/auth.js +18 -6
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 6.4.37
|
|
4
|
+
|
|
5
|
+
- remote + Keycloak: SSO authentication now hydrates the local Feathers client auth store immediately
|
|
6
|
+
- remote handshake can use the configured strategy (for example `sso`) while preserving a coherent local fallback state
|
|
7
|
+
- `useAuth()` exposes separate SSO and Feathers state to avoid mixed `user/token` semantics
|
|
8
|
+
- docs updated for the remote Keycloak contract and backend requirement
|
|
9
|
+
|
|
10
|
+
## 6.4.34
|
|
11
|
+
|
|
12
|
+
- Remote mode: hardened Feathers-Pinia bootstrap by waiting for `nuxtApp.$pinia` before creating the client, avoiding false fallback to raw `$api` during early client initialization.
|
|
13
|
+
- Remote mode: delayed remote auth bootstrap until after Pinia readiness so auth store hydration and Feathers-Pinia stay coherent.
|
|
14
|
+
- DevTools: restored NFZ plume branding from `public/plume-light.png` / `public/plume-dark.png` instead of the embedded fallback icon.
|
|
15
|
+
- DevTools: icon route now varies on `Sec-CH-Prefers-Color-Scheme` and iframe theme sync with parent remains enabled by default.
|
|
16
|
+
|
|
17
|
+
## 6.4.33
|
|
18
|
+
- remote + Keycloak auth sync hardening: the Keycloak plugin no longer marks Pinia auth as authenticated when Feathers `authenticate()` actually failed.
|
|
19
|
+
- remote client bootstrap now mirrors successful `authenticate()` / `reAuthenticate()` results into the Pinia auth store so Feathers session state is visible immediately in remote mode.
|
|
20
|
+
- auth token extraction now accepts `accessToken`, `access_token`, and `token` (including nested `authentication.*` variants) for better IdP/backend interoperability.
|
|
21
|
+
|
|
22
|
+
## 6.4.31
|
|
23
|
+
- Fixed NFZ DevTools icon routing by registering `/__nfz-devtools-icon.png` before the iframe route `/__nfz-devtools` to avoid route capture in consumer apps.
|
|
24
|
+
- Kept parent theme auto-sync logic for the NFZ DevTools iframe.
|
|
25
|
+
|
|
26
|
+
## 6.4.31
|
|
27
|
+
|
|
28
|
+
- DevTools tab icon now uses theme-aware NFZ plume assets via `public/plume-light.png` and `public/plume-dark.png`.
|
|
29
|
+
- NFZ DevTools iframe now applies the parent DevTools theme before first paint to match the active DevTools theme by default.
|
|
30
|
+
- Added a dedicated DevTools icon route `'/__nfz-devtools-icon.svg'` and switched the custom tab icon from Carbon to the NFZ plume.
|
|
31
|
+
|
|
32
|
+
## 6.4.31
|
|
33
|
+
|
|
34
|
+
- Fixed Windows CLI build wrapper by invoking Bun through cmd.exe /d /s /c on Windows and sh -lc elsewhere.
|
|
35
|
+
- Removed fragile direct spawn of bun.cmd that was failing with spawnSync EINVAL on Node 22 / Windows.
|
|
36
|
+
- Kept dist/cli/package.json generation after successful bundle.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## v6.4.17
|
|
40
|
+
- Fixed template-string escaping regressions in `src/cli/core.ts` and `src/runtime/templates/client/plugin.ts`.
|
|
41
|
+
- Restored build/typecheck compatibility for generated Keycloak route middleware and Pinia warning fallback message.
|
|
42
|
+
- Updated `package.json` version to `6.4.17`.
|
|
43
|
+
|
|
44
|
+
- v6.4.16: docs add clear explanation/examples for plugin, server-module, module, client-module, hook, policy CLI targets.
|
|
45
|
+
|
|
46
|
+
## v6.4.15
|
|
47
|
+
- client remote/runtime: when `feathers.client.pinia` is enabled but `nuxtApp.$pinia` is missing, log a clear warning and fall back to the raw Feathers client for `$api` instead of crashing in `createPiniaClient`.
|
|
48
|
+
|
|
49
|
+
## v6.4.31
|
|
50
|
+
|
|
51
|
+
- docs(cli): resynchronized README, FR/EN CLI guides, and CLI reference pages around the public v6.4.31 command surface
|
|
52
|
+
- docs(schema): documented `--validate`, `--repair-auth`, and `--diff` as first-class schema maintenance flags
|
|
53
|
+
- docs(help): clarified `add middleware` target guidance (`nitro` and `route` as public targets, others as advanced)
|
|
54
|
+
- cli(help): refreshed built-in help text and command descriptions for middleware and schema maintenance
|
|
55
|
+
|
|
1
56
|
## v6.4.12
|
|
2
57
|
|
|
3
58
|
- fix(keycloak-sso): clean OIDC callback hashes like `#state=...&session_state=...&code=...` after Keycloak `check-sso` / callback to avoid Vue Router selector warnings in Nuxt 4 consumer apps
|
|
@@ -153,3 +208,35 @@
|
|
|
153
208
|
- Fixed Bun/VitePress docs build flow.
|
|
154
209
|
- Synced NFZ DevTools tab theme with the parent Nuxt DevTools theme by default.
|
|
155
210
|
- Hardened CLI tests, doctor diagnostics, and Mongo management configuration handling.
|
|
211
|
+
|
|
212
|
+
## v6.4.14
|
|
213
|
+
|
|
214
|
+
- CLI: updated `renderAuthKeycloakRouteMiddleware()` so generated Nuxt route middleware now cleans OIDC Keycloak callback hash fragments (`#state=...&session_state=...&code=...`) via `history.replaceState(...)` before auth init.
|
|
215
|
+
- Prevents Vue Router warnings caused by invalid CSS selector hashes after `check-sso` redirects.
|
|
216
|
+
|
|
217
|
+
## v6.4.31
|
|
218
|
+
- Fix CLI build regression in renderAuthKeycloakRouteMiddleware by replacing an invalid nested template literal with string concatenation (`window.location.pathname + window.location.search`).
|
|
219
|
+
- Keep package.json version in sync.
|
|
220
|
+
|
|
221
|
+
## 6.4.31
|
|
222
|
+
- DevTools registration switched from untyped `nuxt.hook('devtools:customTabs', ...)` to `addCustomTab(...)`.
|
|
223
|
+
- NFZ tab icon is now served by the module on `/__nfz-devtools-icon.png` for consumer-app reliability.
|
|
224
|
+
- Keeps parent-theme sync logic for the NFZ DevTools iframe.
|
|
225
|
+
|
|
226
|
+
## 6.4.31
|
|
227
|
+
- DevTools: local icon route now serves an embedded 64x64 plume PNG buffer instead of reading public/plume-light.png at runtime.
|
|
228
|
+
- DevTools: parent theme auto-sync now retries after load and observes both html/body with data-theme and data-color-mode support.
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
## 6.4.32
|
|
232
|
+
- Remote mode + payloadMode=keycloak: Keycloak authenticated state now synchronizes the Feathers client auth session automatically.
|
|
233
|
+
- The auth store is marked authenticated with a token fallback to keycloak.token, and protected services using authentication('jwt') are authorized on boot and token refresh.
|
|
234
|
+
- feathers-auth bootstrap skips reAuthenticate() in remote Keycloak payload mode; keycloak-sso becomes the source of truth for auth session bootstrap.
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
## 6.4.35
|
|
238
|
+
- devtools asset loading made lazy and fault-tolerant to avoid module prepare failure masked as src/module.ts load error
|
|
239
|
+
- public release metadata resynchronized to 6.4.35
|
|
240
|
+
- devtools plume icon/theme-parent behavior preserved
|
|
241
|
+
|
|
242
|
+
- 6.4.37: remote Keycloak Option B contract (`strategy: 'sso'`, `user: loginuser`, `authenticated: true`) is now first-class in the runtime and docs; generated route middleware no longer re-runs `auth.init()` or reuses callback hashes in redirect URIs.
|
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
[v6.4.3] CLI typecheck/test cleanup applied.
|
|
2
|
-
|
|
3
1
|
# nuxt-feathers-zod
|
|
4
2
|
|
|
3
|
+
> OSS reference snapshot: **v6.4.40** — optional Mongo management options aligned and release metadata synchronized.
|
|
4
|
+
|
|
5
5
|
[Documentation](https://vevedh.github.io/nuxt-feathers-zod/)
|
|
6
6
|
|
|
7
7
|
`nuxt-feathers-zod` is the official **Nuxt 4** module that embeds or connects to **FeathersJS v5 (Dove)** with a **CLI-first** workflow and optional **Zod-first** service generation.
|
|
8
8
|
|
|
9
|
-
Current OSS release target: **6.
|
|
9
|
+
Current OSS release target: **6.4.40**.
|
|
10
10
|
|
|
11
11
|
It supports two main usage patterns:
|
|
12
12
|
|
|
@@ -120,7 +120,7 @@ bunx nuxt-feathers-zod add mongodb-compose
|
|
|
120
120
|
bunx nuxt-feathers-zod mongo management --url mongodb://root:change-me@127.0.0.1:27017/app?authSource=admin --auth false
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
-
###
|
|
123
|
+
### Secondary OSS helper commands
|
|
124
124
|
|
|
125
125
|
```bash
|
|
126
126
|
bunx nuxt-feathers-zod templates list
|
|
@@ -132,17 +132,74 @@ bunx nuxt-feathers-zod middlewares list --target nitro
|
|
|
132
132
|
bunx nuxt-feathers-zod middlewares add request-id --target nitro
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
-
## CLI command surface in 6.
|
|
135
|
+
## CLI command surface in 6.4.40
|
|
136
136
|
|
|
137
137
|
| Area | Commands |
|
|
138
138
|
|---|---|
|
|
139
139
|
| Init | `init templates`, `init embedded`, `init remote` |
|
|
140
140
|
| Remote auth | `remote auth keycloak` |
|
|
141
141
|
| Services | `add service`, `add remote-service`, `auth service`, `schema` |
|
|
142
|
-
| Runtime scaffolding | `add middleware`, `add server-module`, `add mongodb-compose`, `mongo management` |
|
|
143
|
-
| OSS
|
|
142
|
+
| Runtime scaffolding | `add middleware`, `schema`, `add server-module`, `add mongodb-compose`, `mongo management` |
|
|
143
|
+
| Secondary OSS helpers | `templates list`, `plugins list/add`, `modules list/add`, `middlewares list/add` |
|
|
144
144
|
| Diagnostics | `doctor` |
|
|
145
145
|
|
|
146
|
+
|
|
147
|
+
## Public CLI focus
|
|
148
|
+
|
|
149
|
+
The public OSS docs now foreground these commands as the **stable core**:
|
|
150
|
+
|
|
151
|
+
- `init embedded`
|
|
152
|
+
- `init remote`
|
|
153
|
+
- `remote auth keycloak`
|
|
154
|
+
- `add service <name>`
|
|
155
|
+
- `add remote-service <name>`
|
|
156
|
+
- `add middleware <name>`
|
|
157
|
+
- `schema <service>`
|
|
158
|
+
- `auth service <name>`
|
|
159
|
+
- `mongo management`
|
|
160
|
+
- `doctor`
|
|
161
|
+
|
|
162
|
+
The following commands remain supported, but are now treated as **secondary helpers or compatibility aliases** in the docs:
|
|
163
|
+
|
|
164
|
+
- `add custom-service <name>`
|
|
165
|
+
- `templates list` / `init templates`
|
|
166
|
+
- `plugins list|add`
|
|
167
|
+
- `modules list|add`
|
|
168
|
+
- `middlewares list|add`
|
|
169
|
+
- `add server-module <name>`
|
|
170
|
+
- `add mongodb-compose`
|
|
171
|
+
|
|
172
|
+
## Schema maintenance quick reference
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
bunx nuxt-feathers-zod schema users --show
|
|
176
|
+
bunx nuxt-feathers-zod schema users --validate
|
|
177
|
+
bunx nuxt-feathers-zod schema users --diff
|
|
178
|
+
bunx nuxt-feathers-zod schema users --repair-auth
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Key `schema` flags:
|
|
182
|
+
|
|
183
|
+
- `--show` / `--json` / `--export`
|
|
184
|
+
- `--set-mode none|zod|json`
|
|
185
|
+
- `--add-field`, `--set-field`, `--remove-field`, `--rename-field`
|
|
186
|
+
- `--validate`
|
|
187
|
+
- `--repair-auth` for auth-enabled `users` baselines
|
|
188
|
+
- `--dry` / `--force`
|
|
189
|
+
|
|
190
|
+
## `add middleware` target matrix
|
|
191
|
+
|
|
192
|
+
| Target | Purpose | Status in docs |
|
|
193
|
+
|---|---|---|
|
|
194
|
+
| `nitro` | Nitro/H3 middleware | public |
|
|
195
|
+
| `route` | Nuxt route middleware in `app/middleware` | public |
|
|
196
|
+
| `feathers` | Feathers server plugin/middleware artifact | advanced |
|
|
197
|
+
| `server-module` | embedded Feathers server module | advanced |
|
|
198
|
+
| `module` | generic module artifact | advanced |
|
|
199
|
+
| `client-module` | client-side module artifact | advanced |
|
|
200
|
+
| `hook` | Feathers hook scaffold | advanced |
|
|
201
|
+
| `policy` | policy/guard scaffold | advanced |
|
|
202
|
+
|
|
146
203
|
## Auth-aware generation for `users`
|
|
147
204
|
|
|
148
205
|
For the `users` service, `--auth` and `--authAware` are distinct concerns:
|
|
@@ -220,6 +277,99 @@ bunx nuxt-feathers-zod add mongodb-compose
|
|
|
220
277
|
bunx nuxt-feathers-zod add mongodb-compose --out docker-compose-db.yaml --database app --rootPassword secret --force
|
|
221
278
|
```
|
|
222
279
|
|
|
280
|
+
## Remote + Keycloak runtime contract
|
|
281
|
+
|
|
282
|
+
In **remote mode** with **Keycloak SSO**, NFZ applies a two-step client contract.
|
|
283
|
+
|
|
284
|
+
### Step 1 — immediate local Feathers session hydration
|
|
285
|
+
|
|
286
|
+
As soon as Keycloak is authenticated in the browser, the client auth store is hydrated immediately:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
authStore.authenticated = true
|
|
290
|
+
authStore.accessToken = keycloak.token
|
|
291
|
+
authStore.user = ssoUser
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
This guarantees that:
|
|
295
|
+
- Nuxt route middleware can consider the user authenticated
|
|
296
|
+
- client service calls receive `Authorization: Bearer <keycloak token>`
|
|
297
|
+
- the local runtime stays coherent even before the remote Feathers API confirms the token
|
|
298
|
+
|
|
299
|
+
### Step 2 — remote authentication handshake
|
|
300
|
+
|
|
301
|
+
NFZ then attempts a remote Feathers authentication call using the configured remote strategy:
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
await api.authenticate({
|
|
305
|
+
strategy: 'sso', // or another configured remote.auth.strategy
|
|
306
|
+
accessToken: keycloak.token,
|
|
307
|
+
access_token: keycloak.token,
|
|
308
|
+
token: keycloak.token,
|
|
309
|
+
user: ssoUser,
|
|
310
|
+
authenticated: true,
|
|
311
|
+
})
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The actual strategy name comes from `feathers.client.remote.auth.strategy`.
|
|
315
|
+
|
|
316
|
+
Example:
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
export default defineNuxtConfig({
|
|
320
|
+
modules: ['nuxt-feathers-zod'],
|
|
321
|
+
feathers: {
|
|
322
|
+
client: {
|
|
323
|
+
mode: 'remote',
|
|
324
|
+
remote: {
|
|
325
|
+
url: 'https://api.example.com',
|
|
326
|
+
auth: {
|
|
327
|
+
enabled: true,
|
|
328
|
+
payloadMode: 'keycloak',
|
|
329
|
+
strategy: 'sso',
|
|
330
|
+
tokenField: 'access_token',
|
|
331
|
+
servicePath: 'authentication',
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
keycloak: {
|
|
336
|
+
serverUrl: 'https://sso.example.com',
|
|
337
|
+
realm: 'myrealm',
|
|
338
|
+
clientId: 'myapp',
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
})
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Resulting semantics in `useAuth()`
|
|
345
|
+
|
|
346
|
+
In provider `keycloak`, `useAuth()` now exposes distinct states:
|
|
347
|
+
|
|
348
|
+
- `isSsoAuthenticated`
|
|
349
|
+
- `isFeathersAuthenticated`
|
|
350
|
+
- `isAuthenticated`
|
|
351
|
+
- `ssoUser`
|
|
352
|
+
- `feathersUser`
|
|
353
|
+
- `user`
|
|
354
|
+
- `ssoToken`
|
|
355
|
+
- `feathersToken`
|
|
356
|
+
- `token`
|
|
357
|
+
|
|
358
|
+
Resolution rules:
|
|
359
|
+
|
|
360
|
+
- `user = feathersUser ?? ssoUser`
|
|
361
|
+
- `token = feathersToken ?? ssoToken`
|
|
362
|
+
|
|
363
|
+
### Backend requirement
|
|
364
|
+
|
|
365
|
+
For protected remote services to be truly accepted by Feathers hooks such as `authenticate('jwt')`, the remote backend must accept the token and strategy you configure.
|
|
366
|
+
|
|
367
|
+
Typical options:
|
|
368
|
+
- validate the Keycloak token directly through the Feathers authentication strategy
|
|
369
|
+
- expose a dedicated remote strategy such as `strategy: 'sso'`
|
|
370
|
+
|
|
371
|
+
If the backend refuses the handshake, NFZ keeps the local client state coherent and preserves the error in the auth store for diagnostics.
|
|
372
|
+
|
|
223
373
|
## Notes
|
|
224
374
|
|
|
225
375
|
- Recommended convention: `servicesDirs: ['services']`
|
|
@@ -249,3 +399,7 @@ This release hardens the CLI patcher for remote mode so chained commands keep `n
|
|
|
249
399
|
|
|
250
400
|
|
|
251
401
|
- 6.4.1: Added automatic Keycloak `js-sha256` default-export shim alias for consumer Nuxt 4 apps to avoid browser crash from `keycloak-js` importing `js-sha256` as a default export.
|
|
402
|
+
|
|
403
|
+
### Remote Keycloak `strategy: 'sso'` with option B
|
|
404
|
+
|
|
405
|
+
When the remote backend expects `api.authenticate({ strategy: 'sso', user: loginuser, authenticated: true })`, NFZ now keeps the local SSO object in the auth store (`authStore.user = ssoUser`) but sends only the derived login string as `user` in the remote authenticate payload.
|
package/dist/cli/index.mjs
CHANGED
|
@@ -2620,7 +2620,7 @@ Commands:
|
|
|
2620
2620
|
remote auth keycloak Configure remote auth payload mode for Keycloak
|
|
2621
2621
|
add service <name> Generate an embedded service (or a service with custom methods via --custom)
|
|
2622
2622
|
add remote-service <name> Register a remote service (client-only)
|
|
2623
|
-
add middleware <name> Generate middleware
|
|
2623
|
+
add middleware <name> Generate middleware or middleware-like artifacts
|
|
2624
2624
|
add mongodb-compose Generate docker-compose-db.yaml for MongoDB
|
|
2625
2625
|
mongo management Enable/update embedded MongoDB management routes
|
|
2626
2626
|
schema <service> Inspect schema state or switch schema mode
|
|
@@ -2777,6 +2777,8 @@ Flags overview:
|
|
|
2777
2777
|
--remove-field <name> remove field from manifest/schema
|
|
2778
2778
|
--set-field <spec> create or replace field definition
|
|
2779
2779
|
--rename-field <from:to> rename field preserving definition
|
|
2780
|
+
--validate validate manifest/schema coherence
|
|
2781
|
+
--repair-auth repair auth-compatible users schema baseline
|
|
2780
2782
|
--servicesDir <dir> (default: services)
|
|
2781
2783
|
--force
|
|
2782
2784
|
--dry
|
|
@@ -5498,12 +5500,31 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
|
|
5498
5500
|
if (import.meta.server)
|
|
5499
5501
|
return
|
|
5500
5502
|
|
|
5503
|
+
const hash = window.location.hash || ''
|
|
5504
|
+
|
|
5505
|
+
// After Keycloak redirects, the hash can contain state/session_state/code which
|
|
5506
|
+
// is not a valid CSS selector. Clean it eagerly so Vue Router does not try to
|
|
5507
|
+
// interpret it as an anchor selector during navigation.
|
|
5508
|
+
if (hash && /(state=|session_state=|code=)/.test(hash)) {
|
|
5509
|
+
const cleanUrl = window.location.pathname + window.location.search
|
|
5510
|
+
window.history.replaceState(window.history.state, '', cleanUrl)
|
|
5511
|
+
}
|
|
5512
|
+
|
|
5501
5513
|
const auth = useAuth()
|
|
5502
5514
|
await auth.init()
|
|
5503
5515
|
|
|
5504
|
-
if (auth.provider.value === 'keycloak' && !auth.
|
|
5505
|
-
await auth.login({ redirectUri: window.location.origin +
|
|
5516
|
+
if (auth.provider.value === 'keycloak' && !auth.isSsoAuthenticated.value) {
|
|
5517
|
+
await auth.login({ redirectUri: window.location.origin + window.location.pathname + window.location.search })
|
|
5518
|
+
return
|
|
5506
5519
|
}
|
|
5520
|
+
|
|
5521
|
+
// Remote + Keycloak contract:
|
|
5522
|
+
// - isSsoAuthenticated means the local Feathers client store is hydrated immediately
|
|
5523
|
+
// with { authenticated:true, accessToken:keycloak.token, user:ssoUser }
|
|
5524
|
+
// - NFZ then attempts api.authenticate(...) using the configured remote strategy
|
|
5525
|
+
// (for example strategy:'sso') and sends { user: loginuser, authenticated: true }
|
|
5526
|
+
// - If the backend does not accept that strategy/token, the client store still remains
|
|
5527
|
+
// coherent for UI/middleware purposes and the error is preserved in the auth store.
|
|
5507
5528
|
})
|
|
5508
5529
|
`;
|
|
5509
5530
|
}
|
|
@@ -6660,7 +6681,7 @@ function createCliCommand() {
|
|
|
6660
6681
|
const addMiddlewareCommand = defineCommand({
|
|
6661
6682
|
meta: {
|
|
6662
6683
|
name: "middleware",
|
|
6663
|
-
description: "Generate middleware"
|
|
6684
|
+
description: "Generate middleware or middleware-like artifacts"
|
|
6664
6685
|
},
|
|
6665
6686
|
args: {
|
|
6666
6687
|
name: { type: "positional", required: true, description: "Middleware name" },
|
|
@@ -6675,7 +6696,7 @@ function createCliCommand() {
|
|
|
6675
6696
|
const addServerModuleCommand = defineCommand({
|
|
6676
6697
|
meta: {
|
|
6677
6698
|
name: "server-module",
|
|
6678
|
-
description: "Generate
|
|
6699
|
+
description: "Generate an embedded server module (advanced)"
|
|
6679
6700
|
},
|
|
6680
6701
|
args: {
|
|
6681
6702
|
name: { type: "positional", required: true, description: "Module name" },
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { createRequire } from 'node:module';
|
|
|
2
2
|
import { addDevServerHandler, defineNuxtModule, createResolver, addImportsDir, addTemplate, addServerPlugin, addServerHandler, addImports, addPlugin, hasNuxtModule } from '@nuxt/kit';
|
|
3
3
|
import { consola } from 'consola';
|
|
4
4
|
import defu from 'defu';
|
|
5
|
-
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { addCustomTab } from '@nuxt/devtools-kit';
|
|
8
8
|
import { eventHandler, setHeader } from 'h3';
|
|
@@ -17,10 +17,35 @@ import { getServerTemplates } from '../dist/runtime/templates/server/index.js';
|
|
|
17
17
|
const DEVTOOLS_ROUTE = "/__nfz-devtools";
|
|
18
18
|
const DEVTOOLS_ROUTE_JSON = "/__nfz-devtools.json";
|
|
19
19
|
const DEVTOOLS_ROUTE_CSS = "/__nfz-devtools.css";
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
);
|
|
20
|
+
const DEVTOOLS_ROUTE_ICON = "/__nfz-devtools-icon.png";
|
|
21
|
+
function tryReadTextAsset(relativePath, fallback = "") {
|
|
22
|
+
try {
|
|
23
|
+
const filePath = fileURLToPath(new URL(relativePath, import.meta.url));
|
|
24
|
+
if (!existsSync(filePath))
|
|
25
|
+
return fallback;
|
|
26
|
+
return readFileSync(filePath, "utf8");
|
|
27
|
+
} catch {
|
|
28
|
+
return fallback;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function tryReadBinaryAsset(relativePath, fallback = new Uint8Array()) {
|
|
32
|
+
try {
|
|
33
|
+
const filePath = fileURLToPath(new URL(relativePath, import.meta.url));
|
|
34
|
+
if (!existsSync(filePath))
|
|
35
|
+
return fallback;
|
|
36
|
+
return readFileSync(filePath);
|
|
37
|
+
} catch {
|
|
38
|
+
return fallback;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function getVendoredStyles() {
|
|
42
|
+
return tryReadTextAsset("./runtime/devtools-ui-kit/assets/styles.css");
|
|
43
|
+
}
|
|
44
|
+
function getDevtoolsIconBuffers() {
|
|
45
|
+
const light = tryReadBinaryAsset("./public/plume-light.png");
|
|
46
|
+
const dark = tryReadBinaryAsset("./public/plume-dark.png", light);
|
|
47
|
+
return { light, dark };
|
|
48
|
+
}
|
|
24
49
|
function safeJson(value) {
|
|
25
50
|
return JSON.stringify(value, null, 2).replace(/</g, "<").replace(/>/g, ">");
|
|
26
51
|
}
|
|
@@ -84,6 +109,52 @@ function renderHtml(payload) {
|
|
|
84
109
|
html.dark { --nfz-bg: #0b1020; --nfz-bg-elevated: rgba(10,15,29,.9); --nfz-card: rgba(17,24,39,.72); --nfz-border: rgba(127,127,127,.22); --nfz-text: #e5e7eb; --nfz-muted: #a1a1aa; }
|
|
85
110
|
html.parent-theme-synced .nfz-sub::after { content: ' \xB7 synced with parent theme'; }
|
|
86
111
|
</style>
|
|
112
|
+
|
|
113
|
+
<script>
|
|
114
|
+
(() => {
|
|
115
|
+
const docEl = document.documentElement
|
|
116
|
+
function applyTheme(theme) {
|
|
117
|
+
docEl.classList.remove('dark', 'light')
|
|
118
|
+
docEl.classList.add(theme)
|
|
119
|
+
}
|
|
120
|
+
function readThemeFromElement(el) {
|
|
121
|
+
if (!el)
|
|
122
|
+
return null
|
|
123
|
+
const className = String(el.className || '')
|
|
124
|
+
const dataTheme = String(el.getAttribute('data-theme') || '')
|
|
125
|
+
const dataColorMode = String(el.getAttribute('data-color-mode') || '')
|
|
126
|
+
const computed = window.parent.getComputedStyle(el)
|
|
127
|
+
const colorScheme = String(computed.colorScheme || '')
|
|
128
|
+
const isDark = /(^|s)dark(s|$)/.test(className)
|
|
129
|
+
|| dataTheme === 'dark'
|
|
130
|
+
|| dataColorMode === 'dark'
|
|
131
|
+
|| colorScheme.includes('dark')
|
|
132
|
+
const isLight = /(^|s)light(s|$)/.test(className)
|
|
133
|
+
|| dataTheme === 'light'
|
|
134
|
+
|| dataColorMode === 'light'
|
|
135
|
+
|| colorScheme.includes('light')
|
|
136
|
+
if (!isDark && !isLight)
|
|
137
|
+
return null
|
|
138
|
+
return { isDark }
|
|
139
|
+
}
|
|
140
|
+
function getParentThemeState() {
|
|
141
|
+
try {
|
|
142
|
+
if (window.parent === window)
|
|
143
|
+
return null
|
|
144
|
+
const parentDoc = window.parent?.document
|
|
145
|
+
return readThemeFromElement(parentDoc?.documentElement) || readThemeFromElement(parentDoc?.body)
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return null
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const parentTheme = getParentThemeState()
|
|
152
|
+
if (parentTheme)
|
|
153
|
+
applyTheme(parentTheme.isDark ? 'dark' : 'light')
|
|
154
|
+
else
|
|
155
|
+
applyTheme(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
|
|
156
|
+
})()
|
|
157
|
+
<\/script>
|
|
87
158
|
</head>
|
|
88
159
|
<body>
|
|
89
160
|
<div class="nfz-shell">
|
|
@@ -195,21 +266,35 @@ function renderHtml(payload) {
|
|
|
195
266
|
})
|
|
196
267
|
|
|
197
268
|
const docEl = document.documentElement
|
|
198
|
-
const rootThemeAttrs = ['class', 'data-theme', 'style']
|
|
269
|
+
const rootThemeAttrs = ['class', 'data-theme', 'data-color-mode', 'style']
|
|
270
|
+
|
|
271
|
+
function readThemeFromElement(el) {
|
|
272
|
+
if (!el)
|
|
273
|
+
return null
|
|
274
|
+
const className = String(el.className || '')
|
|
275
|
+
const dataTheme = String(el.getAttribute('data-theme') || '')
|
|
276
|
+
const dataColorMode = String(el.getAttribute('data-color-mode') || '')
|
|
277
|
+
const computed = window.parent.getComputedStyle(el)
|
|
278
|
+
const colorScheme = String(computed.colorScheme || '')
|
|
279
|
+
const isDark = /(^|s)dark(s|$)/.test(className)
|
|
280
|
+
|| dataTheme === 'dark'
|
|
281
|
+
|| dataColorMode === 'dark'
|
|
282
|
+
|| colorScheme.includes('dark')
|
|
283
|
+
const isLight = /(^|s)light(s|$)/.test(className)
|
|
284
|
+
|| dataTheme === 'light'
|
|
285
|
+
|| dataColorMode === 'light'
|
|
286
|
+
|| colorScheme.includes('light')
|
|
287
|
+
if (!isDark && !isLight)
|
|
288
|
+
return null
|
|
289
|
+
return { isDark }
|
|
290
|
+
}
|
|
199
291
|
|
|
200
292
|
function getParentThemeState() {
|
|
201
293
|
try {
|
|
202
|
-
|
|
203
|
-
if (!parentEl)
|
|
294
|
+
if (window.parent === window)
|
|
204
295
|
return null
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
const computed = window.parent.getComputedStyle(parentEl)
|
|
208
|
-
const colorScheme = String(computed.colorScheme || '')
|
|
209
|
-
const isDark = /(^|s)dark(s|$)/.test(className)
|
|
210
|
-
|| dataTheme === 'dark'
|
|
211
|
-
|| colorScheme.includes('dark')
|
|
212
|
-
return { isDark, className, dataTheme }
|
|
296
|
+
const parentDoc = window.parent?.document
|
|
297
|
+
return readThemeFromElement(parentDoc?.documentElement) || readThemeFromElement(parentDoc?.body)
|
|
213
298
|
}
|
|
214
299
|
catch {
|
|
215
300
|
return null
|
|
@@ -234,24 +319,43 @@ function renderHtml(payload) {
|
|
|
234
319
|
return false
|
|
235
320
|
}
|
|
236
321
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
322
|
+
let parentObservers = []
|
|
323
|
+
function attachParentObservers() {
|
|
324
|
+
try {
|
|
325
|
+
if (window.parent === window || parentObservers.length)
|
|
326
|
+
return
|
|
327
|
+
const parentDoc = window.parent?.document
|
|
328
|
+
const targets = [parentDoc?.documentElement, parentDoc?.body].filter(Boolean)
|
|
329
|
+
parentObservers = targets.map((target) => {
|
|
330
|
+
const observer = new window.MutationObserver(() => syncThemeFromParent())
|
|
331
|
+
observer.observe(target, { attributes: true, attributeFilter: rootThemeAttrs })
|
|
332
|
+
return observer
|
|
333
|
+
})
|
|
245
334
|
}
|
|
335
|
+
catch {}
|
|
246
336
|
}
|
|
247
|
-
|
|
337
|
+
|
|
338
|
+
syncThemeFromParent()
|
|
339
|
+
attachParentObservers()
|
|
340
|
+
;[32, 120, 260, 600, 1200].forEach((delay) => {
|
|
341
|
+
window.setTimeout(() => {
|
|
342
|
+
syncThemeFromParent()
|
|
343
|
+
attachParentObservers()
|
|
344
|
+
}, delay)
|
|
345
|
+
})
|
|
248
346
|
|
|
249
347
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
250
348
|
syncThemeFromParent()
|
|
251
349
|
})
|
|
252
350
|
|
|
351
|
+
window.addEventListener('load', () => {
|
|
352
|
+
syncThemeFromParent()
|
|
353
|
+
attachParentObservers()
|
|
354
|
+
})
|
|
355
|
+
|
|
253
356
|
themeBtn.addEventListener('click', () => {
|
|
254
357
|
syncThemeFromParent()
|
|
358
|
+
attachParentObservers()
|
|
255
359
|
})
|
|
256
360
|
<\/script>
|
|
257
361
|
</body>
|
|
@@ -323,7 +427,7 @@ function setupNfzDevtools(nuxt, options, extras) {
|
|
|
323
427
|
route: DEVTOOLS_ROUTE_CSS,
|
|
324
428
|
handler: eventHandler((event) => {
|
|
325
429
|
setHeader(event, "content-type", "text/css; charset=utf-8");
|
|
326
|
-
return
|
|
430
|
+
return getVendoredStyles();
|
|
327
431
|
})
|
|
328
432
|
});
|
|
329
433
|
addDevServerHandler({
|
|
@@ -333,6 +437,17 @@ function setupNfzDevtools(nuxt, options, extras) {
|
|
|
333
437
|
return payload;
|
|
334
438
|
})
|
|
335
439
|
});
|
|
440
|
+
addDevServerHandler({
|
|
441
|
+
route: DEVTOOLS_ROUTE_ICON,
|
|
442
|
+
handler: eventHandler((event) => {
|
|
443
|
+
const prefersDark = String(event.node.req.headers["sec-ch-prefers-color-scheme"] || "").toLowerCase().includes("dark");
|
|
444
|
+
setHeader(event, "content-type", "image/png");
|
|
445
|
+
setHeader(event, "cache-control", "public, max-age=3600");
|
|
446
|
+
setHeader(event, "vary", "Sec-CH-Prefers-Color-Scheme");
|
|
447
|
+
const icons = getDevtoolsIconBuffers();
|
|
448
|
+
return prefersDark ? icons.dark : icons.light;
|
|
449
|
+
})
|
|
450
|
+
});
|
|
336
451
|
addDevServerHandler({
|
|
337
452
|
route: DEVTOOLS_ROUTE,
|
|
338
453
|
handler: eventHandler((event) => {
|
|
@@ -343,12 +458,12 @@ function setupNfzDevtools(nuxt, options, extras) {
|
|
|
343
458
|
addCustomTab({
|
|
344
459
|
name: "nfz-oss",
|
|
345
460
|
title: "NFZ",
|
|
346
|
-
icon:
|
|
461
|
+
icon: DEVTOOLS_ROUTE_ICON,
|
|
347
462
|
view: {
|
|
348
463
|
type: "iframe",
|
|
349
464
|
src: DEVTOOLS_ROUTE
|
|
350
465
|
}
|
|
351
|
-
}
|
|
466
|
+
});
|
|
352
467
|
}
|
|
353
468
|
|
|
354
469
|
function dedupeStrings(values) {
|
|
@@ -414,7 +529,10 @@ function setTsIncludes(options, nuxt) {
|
|
|
414
529
|
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
415
530
|
nitroConfig.typescript = nitroConfig.typescript || {};
|
|
416
531
|
nitroConfig.typescript.tsConfig = nitroConfig.typescript.tsConfig || {};
|
|
417
|
-
nitroConfig.typescript.tsConfig.include = dedupeStrings([
|
|
532
|
+
nitroConfig.typescript.tsConfig.include = dedupeStrings([
|
|
533
|
+
...nitroConfig.typescript.tsConfig.include || [],
|
|
534
|
+
...includeGlobs
|
|
535
|
+
]);
|
|
418
536
|
});
|
|
419
537
|
}
|
|
420
538
|
async function ensurePinia(client, nuxt) {
|
|
@@ -540,10 +658,10 @@ const module$1 = defineNuxtModule({
|
|
|
540
658
|
);
|
|
541
659
|
if (enableAuthBootstrap) {
|
|
542
660
|
addImports({ from: resolver.resolve("./runtime/stores/auth"), name: "useAuthStore" });
|
|
543
|
-
addPlugin({ order:
|
|
661
|
+
addPlugin({ order: 21, src: resolver.resolve("./runtime/plugins/feathers-auth") });
|
|
544
662
|
}
|
|
545
663
|
if (resolvedOptions.keycloak) {
|
|
546
|
-
addPlugin({ order:
|
|
664
|
+
addPlugin({ order: 22, src: resolver.resolve("./runtime/plugins/keycloak-sso"), mode: "client" });
|
|
547
665
|
}
|
|
548
666
|
}
|
|
549
667
|
let clientPluginDst;
|
|
@@ -554,7 +672,7 @@ const module$1 = defineNuxtModule({
|
|
|
554
672
|
clientPluginDst = tpl.dst;
|
|
555
673
|
}
|
|
556
674
|
addPlugin({
|
|
557
|
-
order:
|
|
675
|
+
order: 20,
|
|
558
676
|
src: clientPluginDst ?? resolver.resolve(resolvedOptions.templateDir, "client/plugin.ts"),
|
|
559
677
|
...mode === "remote" ? { mode: "client" } : {}
|
|
560
678
|
});
|
|
@@ -3,9 +3,15 @@ export declare function useAuth(): {
|
|
|
3
3
|
provider: import("vue").ComputedRef<AuthProvider>;
|
|
4
4
|
ready: import("vue").Ref<boolean, boolean>;
|
|
5
5
|
isAuthenticated: import("vue").ComputedRef<boolean>;
|
|
6
|
+
isSsoAuthenticated: import("vue").ComputedRef<boolean>;
|
|
7
|
+
isFeathersAuthenticated: import("vue").ComputedRef<boolean>;
|
|
6
8
|
user: import("vue").ComputedRef<any>;
|
|
9
|
+
ssoUser: import("vue").ComputedRef<any>;
|
|
10
|
+
feathersUser: import("vue").ComputedRef<any>;
|
|
7
11
|
permissions: import("vue").ComputedRef<any>;
|
|
8
12
|
token: import("vue").ComputedRef<string | null>;
|
|
13
|
+
ssoToken: import("vue").ComputedRef<string | null>;
|
|
14
|
+
feathersToken: import("vue").ComputedRef<string | null>;
|
|
9
15
|
init: () => Promise<void>;
|
|
10
16
|
login: (options?: any) => Promise<any>;
|
|
11
17
|
logout: (options?: any) => Promise<any>;
|