integrate users/me/meta endpoint into the Vue and Nuxt sdks - #60
Conversation
|
Warning Review limit reached
Next review available in: 6 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughNuxt and Vue now support ChangesUser schema profile integration
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant ThunderIDProvider
participant getUsersMeMeta
participant UsersMeAPI
participant UserProfile
ThunderIDProvider->>getUsersMeMeta: Request user schema
getUsersMeMeta->>UsersMeAPI: Request users-me metadata
UsersMeAPI-->>getUsersMeMeta: Return schema metadata
getUsersMeMeta-->>ThunderIDProvider: Provide AttributeSchema map
ThunderIDProvider->>UserProfile: Pass profile and userSchema
UserProfile->>UserProfile: Render fields and validate edits
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts (1)
287-297: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
renderSchemaFieldRowdoes not forward theisSchemaBasedflag.Line 289 calls
shouldShowField(name)with the defaultisSchemaBased = false. Schema-backed fields are therefore filtered a second time againstFIELDS_TO_SKIP, which now containsattributes,isReadOnly, andisReadonly(lines 77-80). TheisSchemaBasedpre-filter at line 489 is then ineffective for those names, and a schema attribute with a matching name never renders.Pass the flag through.
🐛 Proposed fix
- function renderSchemaFieldRow(schema: ExtendedSchema): VNode | null { + function renderSchemaFieldRow(schema: ExtendedSchema, isSchemaBased = false): VNode | null { const {name, displayName, description, mutability, value} = schema; - if (!name || !shouldShowField(name)) return null; + if (!name || !shouldShowField(name, isSchemaBased)) return null;Then call
renderSchemaFieldRow(schema, true)from theuserSchemabranch at line 511.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts` around lines 287 - 297, Update renderSchemaFieldRow to accept and forward the isSchemaBased flag when calling shouldShowField, then invoke it with true from the userSchema rendering branch so schema-backed fields are not filtered by the non-schema field exclusions.
🧹 Nitpick comments (10)
packages/vue/src/components/presentation/user-profile/UserProfile.css.ts (2)
47-57: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueClean up the
imgavatar rule.
border-radius: 50%is declared twice (lines 48 and 52).display: flex,align-items: center, andjustify-content: centerhave no effect on a replacedimgelement. The base.thunderid-user-profile__avatarrule (lines 42-45) already setsborder-radiusandobject-fit, so this rule only needs the explicitwidthandheight.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/components/presentation/user-profile/UserProfile.css.ts` around lines 47 - 57, Clean up the img.thunderid-user-profile__avatar rule by removing the duplicate border-radius declaration and the ineffective display, align-items, and justify-content properties; retain only the explicit width and height overrides, relying on the base .thunderid-user-profile__avatar rule for shared styles.
168-181: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the conflicting
word-breakdeclaration.
white-space: nowrap(line 177) prevents wrapping, soword-break: break-word(line 175) has no effect.display: inline-blockalso makesalign-items: center(line 172) inert. Keep the truncation set (nowrap,overflow: hidden,text-overflow: ellipsis) and drop the two dead declarations.Also consider adding
titleon the value span inBaseUserProfile.tsso truncated values remain readable on hover.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/components/presentation/user-profile/UserProfile.css.ts` around lines 168 - 181, Update the .thunderid-user-profile__field-value styles by removing the ineffective word-break and align-items declarations while preserving the existing truncation properties. In BaseUserProfile, add a title attribute to the value span using its displayed value so truncated content is readable on hover.packages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.ts (1)
203-204: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueThe available-space calculation uses the wrong edge.
'left'alignment pins the panel's left edge to the trigger's left edge, so the panel expands rightward fromrect.left. The usable space is thereforewindow.innerWidth - rect.left, notwindow.innerWidth - rect.right. The current check underestimates the space by the trigger width, so the panel falls back to right alignment in cases where left alignment would still fit.- return window.innerWidth - rect.right >= menuWidth ? 'left' : 'right'; + return window.innerWidth - rect.left >= menuWidth ? 'left' : 'right';🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.ts` around lines 203 - 204, Update the left-alignment space check in the dropdown positioning logic to compare menuWidth against window.innerWidth - rect.left, since the panel expands rightward from the trigger’s left edge. Preserve the existing preference for left alignment when it fits and the right-alignment fallback otherwise.packages/vue/src/api/getUsersMeMeta.ts (1)
39-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the shared HTTP-to-
Responseadapter.This
defaultFetcherbody duplicates the adapters inpackages/vue/src/api/getUsersMe.tsandpackages/vue/src/api/updateMeProfile.ts. Only the method default and the request payload differ. Move the adapter into one shared helper, ideally in@thunderid/javascriptor@thunderid/browser, and reuse it here.Before adding a helper to a framework package, check whether it belongs in a lower package in the dependency hierarchy.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/api/getUsersMeMeta.ts` around lines 39 - 54, Extract the shared HTTP-to-Response conversion currently implemented in defaultFetcher into a reusable helper in the lowest appropriate package, preferably `@thunderid/javascript` or `@thunderid/browser`, then update defaultFetcher and the corresponding adapters in getUsersMe and updateMeProfile to reuse it while preserving their distinct HTTP method defaults and request payloads.Source: Coding guidelines
packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts (2)
217-244: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
saveFieldexits edit mode before the update resolves.
props.onUpdatereturns aPromise. Line 244 clears the editing flag synchronously, so the row returns to display mode while the request is still running. If the request fails, the row shows the stale value and the field-level error area at line 317 stays empty.Await the update, and close the editor only after it succeeds.
♻️ Proposed refactor
- function saveField(schema: ExtendedSchema): void { + async function saveField(schema: ExtendedSchema): Promise<void> { @@ fieldErrors.value = {...fieldErrors.value, [fieldName]: ''}; const payload: Record<string, unknown> = buildPatchValue(fieldName, value, schema.schemaId, schema.multiValued); - props.onUpdate(payload); - editingFields.value = {...editingFields.value, [fieldName]: false}; + try { + await props.onUpdate(payload); + editingFields.value = {...editingFields.value, [fieldName]: false}; + } catch (updateError: unknown) { + fieldErrors.value = { + ...fieldErrors.value, + [fieldName]: updateError instanceof Error ? updateError.message : `Failed to update ${fieldLabel}.`, + }; + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts` around lines 217 - 244, Update saveField to be async and await the Promise returned by props.onUpdate before clearing editingFields for the field. Keep the editor open when the update rejects, allowing the existing field-level error handling to report the failure, and only close it after a successful update.
400-406: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winHandle a broken avatar image URL.
pictureis rendered directly intoimg.src. If the URL is unreachable, the browser shows a broken-image icon and the gradient-initials fallback never appears. Add anonErrorhandler that clears the resolved picture and re-renders the initials avatar.The same pattern exists in
packages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.ts(lines 246-259 and 280-286).Also applies to: 424-434
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts` around lines 400 - 406, Update BaseUserProfile’s picture rendering and related avatar markup to handle image load errors: add an onError handler that clears the resolved picture and triggers the initials fallback when the URL is unreachable. Follow the existing implementation pattern in BaseUserDropdown, and apply the same behavior to both referenced avatar render paths.packages/vue/src/providers/ThunderIDProvider.ts (1)
247-275: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winRun the two fetches in parallel and avoid refetching static schema metadata.
getUsersMeandgetUsersMeMetaare independent, but they run sequentially. This doubles the latency of every session update.revalidateProfilenow callsupdateSession()(line 526), so both requests run again after every single field save inBaseUserProfile. The schema metadata is static per tenant, so the repeated/users/me/metacall adds no value.Run both requests concurrently. Fetch the schema only when
userSchema.valueis stillnull.♻️ Proposed refactor
if (currentSignInStatus && shouldFetchProfile) { - try { - const fetchedProfile: User = await getUsersMe({ - baseUrl, - url: resolveResourceEndpoint('usersMe', {endpoints: props.endpoints}), - instanceId: props.instanceId, - }); - profileData = {...claims, ...fetchedProfile}; - } catch { - // silent failure, fall back to token claims - } - - try { - const metaRes = await getUsersMeMeta({ - baseUrl, - url: resolveResourceEndpoint('usersMeMeta', {endpoints: props.endpoints}), - instanceId: props.instanceId, - }); - if (metaRes?.schema) { - userSchema.value = metaRes.schema; - } else { - userSchema.value = null; - } - } catch { - userSchema.value = null; - } + const profilePromise: Promise<User | null> = getUsersMe({ + baseUrl, + url: resolveResourceEndpoint('usersMe', {endpoints: props.endpoints}), + instanceId: props.instanceId, + }).catch(() => null); + + const metaPromise: Promise<UsersMeMetaResponse | null> = userSchema.value + ? Promise.resolve(null) + : getUsersMeMeta({ + baseUrl, + url: resolveResourceEndpoint('usersMeMeta', {endpoints: props.endpoints}), + instanceId: props.instanceId, + }).catch(() => null); + + const [fetchedProfile, metaRes] = await Promise.all([profilePromise, metaPromise]); + + if (fetchedProfile) { + profileData = {...claims, ...fetchedProfile}; + } + if (metaRes) { + userSchema.value = metaRes.schema ?? null; + } } else { userSchema.value = null; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/providers/ThunderIDProvider.ts` around lines 247 - 275, Update the currentSignInStatus/shouldFetchProfile branch to start getUsersMe and, only when userSchema.value is null, getUsersMeMeta concurrently rather than awaiting them sequentially. Preserve the existing profile fallback and schema error handling, and skip the metadata request when userSchema.value is already populated.packages/vue/src/components/presentation/user-dropdown/UserDropdown.css.ts (1)
92-98: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueThe
imgrules duplicate the base rules.
img.thunderid-user-dropdown__avatarsetsobject-fit: coverandborder-radius: 50%, but the base.thunderid-user-dropdown__avatarrule already sets both (lines 84 and 92). The same duplication exists forimg.thunderid-user-dropdown__menu-header-avataragainst lines 227 and 234. Bothimgblocks can be removed.The
max-width/max-heightpairs at lines 82-83, 104-105, and 113-114 also restate the existingwidth/heightvalues. Keep them only if a specific image intrinsic-size overflow was observed.Also applies to: 234-240
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/components/presentation/user-dropdown/UserDropdown.css.ts` around lines 92 - 98, Remove the redundant img.thunderid-user-dropdown__avatar and img.thunderid-user-dropdown__menu-header-avatar rules because their object-fit and border-radius declarations duplicate the base avatar styles. Also remove the redundant max-width/max-height declarations that merely restate the corresponding width/height values, unless they are required to prevent observed intrinsic image overflow.packages/vue/src/components/presentation/user-profile/UserProfile.ts (1)
83-90: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
deepMergefor preference resolution.
deepMergeis already imported in this file. The manual spread only merges theuserbranch. If a consumer passes a partialthemeori18nobject inprops.preferences, that branch replaces the context value entirely instead of merging.- const resolvedPreferences = computed(() => ({ - ...contextPreferences, - ...props.preferences, - user: { - ...contextPreferences?.user, - ...props.preferences?.user, - }, - })); + const resolvedPreferences = computed<Preferences>(() => deepMerge({...(contextPreferences ?? {})}, props.preferences));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/components/presentation/user-profile/UserProfile.ts` around lines 83 - 90, Update the resolvedPreferences computed value to use the imported deepMerge utility instead of manual object spreads, merging contextPreferences with props.preferences so nested preference branches such as user, theme, and i18n are preserved and combined.packages/vue/src/providers/UserProvider.ts (1)
66-68: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winDrop the nonexistent inline fallback for
profile.userSchema.
UserProvideracceptsuserSchemaas its own prop, and both current producers pass it separately. The(props.profile as any)?.userSchemabranch can never resolve to a sourced value, and theas anyhides theUserProfileshape mismatch. Useprops.userSchema ?? null.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/providers/UserProvider.ts` around lines 66 - 68, Update the userSchemaRef computed value in UserProvider to use only props.userSchema, falling back to null when absent; remove the profile.userSchema access and its any cast.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/nuxt/src/runtime/server/ThunderIDNuxtClient.ts`:
- Around line 219-235: Update ThunderIDNuxtClient.getUserSchema to preserve
schema-fetch failures by rejecting errors or returning an explicit error result
instead of converting every failure to null; retain null only for a valid
response without schema. In
packages/nuxt/src/runtime/server/plugins/thunderid-ssr.ts#L165-L167, handle the
selected failure contract in the Promise.allSettled result and log schema
request failures.
In `@packages/nuxt/src/runtime/utils/stateKeys.ts`:
- Around line 25-31: Update getUserSchemaStateKey to derive its vendor prefix
through the shared getVendorPrefix(vendor) resolver instead of applying the
local default directly, while preserving the existing user-schema key format and
default behavior.
In `@packages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.ts`:
- Around line 116-122: The avatar URL candidate list is duplicated and
incorrectly treats the OIDC profile URL as an image; create one shared resolver
in the lowest suitable package, excluding profile and preserving the fallback to
attributes.picture, then replace the inline resolution in BaseUserDropdown.ts
(116-122) and BaseUserProfile.ts (400-406) with that resolver. Neither site
requires separate logic; both must use the same resolver and retain
gradient-initials fallback when image loading fails.
- Around line 380-407: Update the teleported profile modal rendering and
handleKeyDown to provide dialog semantics: role="dialog", aria-modal="true", and
an accessible name. On modal open, move focus to the modal or close button; trap
Tab and Shift+Tab within modal controls, and close on Escape. Wrap the existing
props.onProfileModalClose path so closing restores focus to the dropdown
trigger, including overlay and close-button dismissal.
In `@packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts`:
- Around line 229-239: Update the regex validation in BaseUserProfile to bound
the server-supplied schema.regex length before compiling or testing it,
preventing pathological patterns from freezing the UI. Cache the compiled RegExp
per field instead of rebuilding it on every save, while preserving
invalid-pattern pass-through behavior. In the catch block, add a console.warn
containing the field context and compilation error for diagnosability.
- Around line 224-234: Update BaseUserProfile validation messages and the
Save/Cancel labels to use the SDK i18n layer via useI18n, passing translation
keys with the current English text as fallbacks. Apply this to the required and
regex errors (including the additional referenced occurrences), and use “has an
invalid format” instead of “is invalid format.”
- Around line 508-513: Update the metaSchemas mapping in the field-row rendering
flow to reuse the value already resolved from editedValues and the current
user’s attributes/top-level fields, rather than recomputing
currentUser[schema.name]. Preserve that computed value when passing the schema
to renderSchemaFieldRow so nested attributes and in-progress edits remain
visible.
In `@packages/vue/src/components/presentation/user-profile/UserProfile.css.ts`:
- Around line 13-23: Update the .thunderid-user-profile rule to replace the
fixed 600px min-width with a responsive constraint that fits its parent and
small viewports. Preserve the existing compact modifier behavior and prevent
horizontal overflow within the UserDropdown modal.
---
Outside diff comments:
In `@packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts`:
- Around line 287-297: Update renderSchemaFieldRow to accept and forward the
isSchemaBased flag when calling shouldShowField, then invoke it with true from
the userSchema rendering branch so schema-backed fields are not filtered by the
non-schema field exclusions.
---
Nitpick comments:
In `@packages/vue/src/api/getUsersMeMeta.ts`:
- Around line 39-54: Extract the shared HTTP-to-Response conversion currently
implemented in defaultFetcher into a reusable helper in the lowest appropriate
package, preferably `@thunderid/javascript` or `@thunderid/browser`, then update
defaultFetcher and the corresponding adapters in getUsersMe and updateMeProfile
to reuse it while preserving their distinct HTTP method defaults and request
payloads.
In `@packages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.ts`:
- Around line 203-204: Update the left-alignment space check in the dropdown
positioning logic to compare menuWidth against window.innerWidth - rect.left,
since the panel expands rightward from the trigger’s left edge. Preserve the
existing preference for left alignment when it fits and the right-alignment
fallback otherwise.
In `@packages/vue/src/components/presentation/user-dropdown/UserDropdown.css.ts`:
- Around line 92-98: Remove the redundant img.thunderid-user-dropdown__avatar
and img.thunderid-user-dropdown__menu-header-avatar rules because their
object-fit and border-radius declarations duplicate the base avatar styles. Also
remove the redundant max-width/max-height declarations that merely restate the
corresponding width/height values, unless they are required to prevent observed
intrinsic image overflow.
In `@packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts`:
- Around line 217-244: Update saveField to be async and await the Promise
returned by props.onUpdate before clearing editingFields for the field. Keep the
editor open when the update rejects, allowing the existing field-level error
handling to report the failure, and only close it after a successful update.
- Around line 400-406: Update BaseUserProfile’s picture rendering and related
avatar markup to handle image load errors: add an onError handler that clears
the resolved picture and triggers the initials fallback when the URL is
unreachable. Follow the existing implementation pattern in BaseUserDropdown, and
apply the same behavior to both referenced avatar render paths.
In `@packages/vue/src/components/presentation/user-profile/UserProfile.css.ts`:
- Around line 47-57: Clean up the img.thunderid-user-profile__avatar rule by
removing the duplicate border-radius declaration and the ineffective display,
align-items, and justify-content properties; retain only the explicit width and
height overrides, relying on the base .thunderid-user-profile__avatar rule for
shared styles.
- Around line 168-181: Update the .thunderid-user-profile__field-value styles by
removing the ineffective word-break and align-items declarations while
preserving the existing truncation properties. In BaseUserProfile, add a title
attribute to the value span using its displayed value so truncated content is
readable on hover.
In `@packages/vue/src/components/presentation/user-profile/UserProfile.ts`:
- Around line 83-90: Update the resolvedPreferences computed value to use the
imported deepMerge utility instead of manual object spreads, merging
contextPreferences with props.preferences so nested preference branches such as
user, theme, and i18n are preserved and combined.
In `@packages/vue/src/providers/ThunderIDProvider.ts`:
- Around line 247-275: Update the currentSignInStatus/shouldFetchProfile branch
to start getUsersMe and, only when userSchema.value is null, getUsersMeMeta
concurrently rather than awaiting them sequentially. Preserve the existing
profile fallback and schema error handling, and skip the metadata request when
userSchema.value is already populated.
In `@packages/vue/src/providers/UserProvider.ts`:
- Around line 66-68: Update the userSchemaRef computed value in UserProvider to
use only props.userSchema, falling back to null when absent; remove the
profile.userSchema access and its any cast.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 0300dac9-dc66-442f-bb3f-7ff96ccca54c
📒 Files selected for processing (21)
packages/nuxt/src/module.tspackages/nuxt/src/runtime/components/ThunderIDRoot.tspackages/nuxt/src/runtime/plugins/thunderid.tspackages/nuxt/src/runtime/server/ThunderIDNuxtClient.tspackages/nuxt/src/runtime/server/plugins/thunderid-ssr.tspackages/nuxt/src/runtime/server/utils/session.tspackages/nuxt/src/runtime/types.tspackages/nuxt/src/runtime/utils/stateKeys.tspackages/nuxt/tests/unit/define-thunderid-middleware.test.tspackages/nuxt/tests/unit/thunderid-ssr.test.tspackages/vue/src/api/getUsersMeMeta.tspackages/vue/src/api/updateMeProfile.tspackages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.tspackages/vue/src/components/presentation/user-dropdown/UserDropdown.css.tspackages/vue/src/components/presentation/user-profile/BaseUserProfile.tspackages/vue/src/components/presentation/user-profile/UserProfile.css.tspackages/vue/src/components/presentation/user-profile/UserProfile.tspackages/vue/src/index.tspackages/vue/src/models/contexts.tspackages/vue/src/providers/ThunderIDProvider.tspackages/vue/src/providers/UserProvider.ts
There was a problem hiding this comment.
Pull request overview
Integrates the /users/me/meta schema endpoint into the Vue (@thunderid/vue) and Nuxt (@thunderid/nuxt) SDKs so user-profile UI can render schema-driven fields and Nuxt can SSR-hydrate both the user profile and its schema metadata.
Changes:
- Vue: fetches
/users/me+/users/me/metaduring session update, threadsuserSchemathrough providers/composables, and renders schema-driven profile fields with validation. - Nuxt: adds SSR parallel fetch for user schema, hydrates it into
useState, and exposes it through<ThunderIDRoot>/UserProvider. - UI: updates avatar rendering (image fallback) and adjusts UserProfile/UserDropdown styling and modal behavior.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/vue/src/providers/UserProvider.ts | Adds userSchema to the User context and wires it from props into provide() context. |
| packages/vue/src/providers/ThunderIDProvider.ts | Fetches /users/me and /users/me/meta (gated by preferences) and passes userSchema downstream. |
| packages/vue/src/models/contexts.ts | Extends Vue context types to include preferences and userSchema. |
| packages/vue/src/index.ts | Exports the new Vue getUsersMeMeta helper. |
| packages/vue/src/components/presentation/user-profile/UserProfile.ts | Adds preferences merging, gates editability when profile fetch is disabled, and supports updateProfile callback + schema-driven profile editing. |
| packages/vue/src/components/presentation/user-profile/UserProfile.css.ts | Updates UserProfile styling to align closer to React parity. |
| packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts | Implements schema-driven field rendering, required/regex validation, and avatar image fallback. |
| packages/vue/src/components/presentation/user-dropdown/UserDropdown.css.ts | Updates avatar sizing/object-fit and modal sizing/layout. |
| packages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.ts | Adds avatar image support and teleports the profile modal overlay to body. |
| packages/vue/src/api/updateMeProfile.ts | Switches default update method to PUT. |
| packages/vue/src/api/getUsersMeMeta.ts | Adds Vue API wrapper for /users/me/meta using SPA FetchHttpClient. |
| packages/nuxt/tests/unit/thunderid-ssr.test.ts | Updates SSR unit test mocking to include getUserSchema(). |
| packages/nuxt/tests/unit/define-thunderid-middleware.test.ts | Extends Nuxt #app mocks to include useRuntimeConfig(). |
| packages/nuxt/src/runtime/utils/stateKeys.ts | Adds a vendor-scoped state key for SSR-hydrated user schema. |
| packages/nuxt/src/runtime/types.ts | Refactors ThunderIDNuxtConfig to extend AuthClientConfig and adds SSR userSchema typing. |
| packages/nuxt/src/runtime/server/utils/session.ts | Makes session cookie name helpers vendor-aware via CookieConfig helpers. |
| packages/nuxt/src/runtime/server/ThunderIDNuxtClient.ts | Adds server-side helpers for fetching user/profile/schema and updating the profile via /users/me. |
| packages/nuxt/src/runtime/server/plugins/thunderid-ssr.ts | Fetches userSchema concurrently during SSR and injects into event.context. |
| packages/nuxt/src/runtime/plugins/thunderid.ts | Hydrates userSchema into vendor-scoped useState on server. |
| packages/nuxt/src/runtime/components/ThunderIDRoot.ts | Passes SSR-hydrated userSchema into Vue UserProvider. |
| packages/nuxt/src/module.ts | Exposes endpoints through Nuxt runtime config and module types. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3df13f1 to
1d1eafb
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts`:
- Line 247: Remove the hardcoded “[ThunderID]” prefix from the warning in the
user schema regular-expression validation path, or replace it with the
configured value from getVendorPrefix(vendor). Update the console.warn call
without changing its existing error details or behavior.
- Around line 224-254: The saveField function currently closes the editor
without waiting for the asynchronous props.onUpdate result. Make saveField
asynchronous, await props.onUpdate(payload), and move the editingFields update
so it runs only after the update succeeds, allowing failures to keep the field
in edit mode.
- Around line 359-367: Update the Button configuration in BaseUserProfile’s
edit-button render to use supported ButtonProps values: replace the invalid
tertiary color and icon variant with valid color and variant values, and
represent the edit icon through startIcon or endIcon instead of variant.
- Around line 227-253: Update the payload construction in the field-validation
flow to submit the normalized trimmed string value (strVal) rather than the
original value, while preserving existing handling for empty and non-string
values as required by buildPatchValue and multiValued fields.
- Around line 216-221: Update cancelEditing to restore nested attribute values
using the same lookup precedence as schema rendering: when the requested field
is absent at the profile top level, read it from data.attributes before falling
back to an empty string. Preserve existing top-level restoration and
editing/error state updates.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 166fa4f6-9a8f-4a10-9552-033862444dbe
📒 Files selected for processing (6)
packages/nuxt/src/runtime/server/ThunderIDNuxtClient.tspackages/nuxt/src/runtime/utils/stateKeys.tspackages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.tspackages/vue/src/components/presentation/user-profile/BaseUserProfile.tspackages/vue/src/components/presentation/user-profile/UserProfile.css.tspackages/vue/src/components/presentation/user-profile/UserProfile.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/nuxt/src/runtime/server/ThunderIDNuxtClient.ts
- packages/vue/src/components/presentation/user-profile/UserProfile.css.ts
- packages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.ts
- packages/vue/src/components/presentation/user-profile/UserProfile.ts
1d1eafb to
b7060fe
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts (2)
220-226: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse the schema value precedence when cancelling.
Line 223 reads the top-level field before
attributes. Line 522 rendersattributesbefore the top-level field. If both values exist, Cancel restores one value but the row renders another value.Proposed fix
- (data as Record<string, any>)?.[fieldName] ?? (data as any)?.attributes?.[fieldName] ?? ''; + (data as any)?.attributes?.[fieldName] ?? (data as Record<string, any>)?.[fieldName] ?? '';🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts` around lines 220 - 226, Update cancelEditing so originalValue reads data.attributes[fieldName] before the top-level data[fieldName], matching the rendering precedence used by the component and ensuring Cancel restores the displayed value when both exist.
244-265: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winValidate regex safety before browser execution.
The 250-character limit and cache do not prevent catastrophic-backtracking patterns. A schema regex can still block the browser main thread during
reg.test(strVal). Enforce regex safety at the schema source, or avoid executing tenant-defined regexes in the client.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts` around lines 244 - 265, Update the regex validation flow in the user-profile field handling around regexCache and reg.test so tenant-defined schema patterns cannot execute unsafely in the browser. Prefer validating or rejecting regexes at the schema source; otherwise remove client-side execution of tenant-provided patterns while preserving the existing invalid-format behavior for safe validation.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.ts`:
- Around line 179-224: Scope modal focus and close handling to the current
BaseUserDropdown instance by storing its teleported modal overlay in a component
ref and using that ref instead of document.querySelector in handleKeyDown and
the isProfileModalOpen watcher. Query the close button from the
instance-specific overlay ref, and ensure Escape only closes this instance’s
modal without affecting other open dropdowns.
- Around line 285-298: Update the avatar rendering in BaseUserDropdown to track
image-load failure reactively, attach an error handler to the rendered img, and
reset the failure state whenever picture changes. When loading fails, render the
existing gradient initials span instead of the broken image, while preserving
the current picture-based rendering for successful loads.
- Around line 242-243: Update the auto-alignment logic in BaseUserDropdown to
compare available viewport space on both sides of the trigger and choose the
side with more room, preserving the documented menuAlign="auto" contract. Do not
retain the current right-space-only, left-preferred condition unless the public
documentation is intentionally changed instead.
In `@packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts`:
- Around line 385-393: Replace the hardcoded "Edit" title in the Button
configuration within BaseUserProfile with the existing t translation helper,
matching the localization pattern used by adjacent actions while leaving the
button behavior unchanged.
---
Duplicate comments:
In `@packages/vue/src/components/presentation/user-profile/BaseUserProfile.ts`:
- Around line 220-226: Update cancelEditing so originalValue reads
data.attributes[fieldName] before the top-level data[fieldName], matching the
rendering precedence used by the component and ensuring Cancel restores the
displayed value when both exist.
- Around line 244-265: Update the regex validation flow in the user-profile
field handling around regexCache and reg.test so tenant-defined schema patterns
cannot execute unsafely in the browser. Prefer validating or rejecting regexes
at the schema source; otherwise remove client-side execution of tenant-provided
patterns while preserving the existing invalid-format behavior for safe
validation.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: be2ef43d-d00c-44d9-bff7-ea8f978978fe
📒 Files selected for processing (2)
packages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.tspackages/vue/src/components/presentation/user-profile/BaseUserProfile.ts
2ccd992 to
f6ae91e
Compare
f6ae91e to
93d0cbb
Compare
Purpose
Integrates the /users/me/meta schema endpoint into the React (@thunderid/vue) and Next.js (@thunderid/nuxt) SDKs. This allows the component to dynamically load user profile schema metadata (attribute types, labels, required states, and order) directly from the ThunderID server
Approach
Vue SDK (
@thunderid/vue):getUsersMeMetaAPI client helper for fetching profile schema attributes from/users/me/meta.ThunderIDProviderandUserProviderto fetch schema metadata and supplyuserSchemaStatevia Vue'sprovide/injectcontext.<BaseUserProfile>to render schema driven form fields dynamically based on attribute metadata with regex validation.<BaseUserDropdown>and<BaseUserProfile>Nuxt SDK (
@thunderid/nuxt):ThunderIDNuxtConfigto extendAuthClientConfigdirectly from@thunderid/node.getUserSchema()toThunderIDNuxtClientand integrated concurrent schema fetching into the Nitro SSR server plugin (thunderid-ssr.ts).userSchemaStatefrom SSR payloads into client state via<ThunderIDRoot>.usersMe,usersMeMeta) fromnuxt.config.tstoThunderIDNuxtClientand Nitro SSR plugins.Related Issues
/users/me/metaschema endpoint into SDKs for field validation on profile updates thunderid#4625Related PRs
Checklist
breaking changelabel added.Security checks
Summary by CodeRabbit
New Features
Bug Fixes