chore: release v0.2.0#112
Conversation
Fix getManageable to use guilds.getMember(guild.id, userId) instead of users.getGuildMember(guild.id) which incorrectly used the bot token on an OAuth-only endpoint. When member fetch fails, fall back to OAuth permissions instead of returning false. Fix isAdmin to handle missing member.permissions field by accepting oauthPermissions as fallback. Fix PermissionNodeManager to properly await async refresh() before run() executes, preventing race condition on first use. Co-authored-by: RedStar071 <76824516+RedStar071@users.noreply.github.com>
Co-authored-by: RedStar071 <76824516+RedStar071@users.noreply.github.com>
The profile page was hiding guilds where the user has `MANAGE_GUILD` but
isn't the owner — unlike the old site which displayed them. Two root
causes:
### `server/utils/discord.ts`
- **`getManageable()`** called `users.getGuildMember(guild.id)` — an
OAuth-only endpoint — with the **bot token**, silently failing for every
guild the bot is in. Changed to `guilds.getMember(guild.id, userId)` and
falls back to OAuth `permissions` bitfield when the member fetch fails
instead of returning `false`.
```diff
- const member = await useApi().users.getGuildMember(guild.id).catch(() => undefined);
- if (!member) return false;
+ const member = await useApi().guilds.getMember(guild.id, userId).catch(() => undefined);
+ if (!member) return hasManageGuild;
```
- **`isAdmin()`** now accepts optional `oauthPermissions: bigint`
fallback for when the bot-fetched member object lacks the `permissions`
field (only present in OAuth member responses).
- **`manage()`** threads `oauthPermissions` through to `isAdmin()`.
### `server/database/settings/structures/PermissionNodeManager.ts`
- `refresh()` is async but was fire-and-forget in the constructor —
`run()` could execute against an empty `sorted` Collection, making the
`conf` command permission node check a no-op (`null ?? true` → always
`true`).
- Store the refresh promise in `#refreshPromise` and `await` it at the
top of `run()`. On rejection, clear the stored promise so subsequent
calls aren't stuck on a stale error.
```typescript
public async run(member: APIGuildMember, command: WolfCommand) {
if (this.#refreshPromise) {
try { await this.#refreshPromise; }
catch { this.#refreshPromise = null; }
}
return (await this.runUser(member, command)) ?? (await this.runRole(member, command));
}
```
`canManage()` (API route handlers) is unaffected — it receives members
from `getCurrentMember()` which already uses the user's OAuth token.
<!-- START COPILOT ORIGINAL PROMPT -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>request: synchronize server view permissions in profile
as same behavior of old site</issue_title>
> <issue_description>### Is there an existing issue or pull request for
this?
>
> - [x] I have searched the existing issues and pull requests
>
> ### Feature description
>
> allows you to view servers even if you are not the owner or do not
have `ADMINISTRATOR` permissions, as shown here:
>
> Old Site:
> <img width="1383" height="139" alt="Image"
src="https://github.com/user-attachments/assets/9fc02b2d-b7ca-4e7a-8747-3cff16321d92"
/>
> New Site (missing the servers no has support a like old behavior):
> <img width="1412" height="672" alt="Image"
src="https://github.com/user-attachments/assets/1ebde389-8cda-463b-8a33-58919af9e6a3"
/>
>
> ### Desired solution
>
> support old behavior for managing server no has owner or role with
permission `ADMINISTRATOR`
>
> ### Alternatives considered
>
> no alternative i has considered
>
> ### Additional context
>
> _No response_</issue_description>
>
> <agent_instructions>fix NodePermissionsManager and file
server/utils/discord.ts</agent_instructions>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>
</details>
> **Custom agent used: wolfstar-dev-agent**
> WolfStar Development Agent - Enhanced with Context7 MCP and Beast Mode
<!-- START COPILOT CODING AGENT SUFFIX -->
- Fixes #67
<!-- START COPILOT CODING AGENT TIPS -->
---
📍 Connect Copilot coding agent with [Jira](https://gh.io/cca-jira-docs),
[Azure Boards](https://gh.io/cca-azure-boards-docs) or
[Linear](https://gh.io/cca-linear-docs) to delegate work to Copilot in
one click without leaving your project management tool.
) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Consolidated developer tooling and scripts into a unified vite-plus workflow, updated CI flows, pinned workspace overrides, and adjusted package lifecycle hooks. * Moved pre-commit staging to a new hook and updated editor recommendations/settings. * **Tests** * Centralized test configuration into the new Vite-based setup and standardized test invocations across CI. * **Style** * Widespread non-functional whitespace and formatting cleanup across many components and pages. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: RedStar071 <76824516+RedStar071@users.noreply.github.com> Agent-Logs-Url: https://github.com/wolfstar-project/wolfstar.rocks/sessions/56b639db-59a6-49c6-add7-980c76e72c38
🔍 Code Quality Overview —
|
| File | Change |
|---|---|
app/app.config.ts |
73 lines removed — footer column definitions extracted out |
app/components/app/Footer.vue |
Now consumes useFooter() composable instead of inline config |
Summary
A clean, focused refactor. The static footer navigation links (Support Server, GitHub, Blog, Donate, Terms, Privacy) were previously defined in app.config.ts and are now owned by the dedicated app/composables/useFooter.ts composable. Footer.vue is updated to call useFooter() and destructure columns from it. app.config.ts is now minimal — only the UI theme is defined there.
What's good:
- Separation of concerns is improved; footer link configuration is now colocated with the composable that owns it.
app.config.tsis cleaner and focused on UI theming only.- The destructuring in
Footer.vue(const { columns } = useFooter()) is idiomatic.
Minor observation (non-blocking):
The columns array in useFooter.ts is fully static — it has no reactive dependencies. Wrapping it in computed<FooterColumn[]>(() => [...]) adds a reactivity layer that Vue will compute once and cache but never invalidate. A simple top-level const would be slightly more efficient and clearer in intent:
// Current
const columns = computed<FooterColumn[]>(() => [...]);
// Consider
const columns: FooterColumn[] = [...];This is only worth changing if the columns ever need to depend on reactive state (e.g., i18n, auth status); if that's anticipated, keeping computed makes sense as a forward-compatible choice.
CI Status
CI checks are still in progress for this commit. The previous commit's CI (6bdfbfb8) completed with ✅ all checks passing (lint, tests, contribution guidelines, code quality). No regressions are expected from this focused refactor, but confirm CI green before merging to release.
Generated by WolfStar Code Quality Reviewer · ◷
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated build and test configuration files to use modern JavaScript module utilities for determining the project root directory. This improves code maintainability and ensures better compatibility with current Node.js development standards. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
🔍 Code Quality Overview —
|
| File | Type | Risk |
|---|---|---|
.gitignore |
Config / housekeeping | 🟢 None |
This is a pure housekeeping commit — removes a duplicate .nvmrc entry from .gitignore. No source code, components, composables, server routes, or tests were modified. No correctness, performance, or accessibility concerns.
🚦 CI Status (at time of review)
| Job | Status |
|---|---|
| 🔠 Lint project | ✅ Passed |
| 🧪 Unit tests | ✅ Passed |
| 🧹 Unused code check | ✅ Passed |
| 🧪 Component tests | ✅ Passed |
| 🛡️ GitGuardian Security | ✅ Passed |
| 🖥️ Browser tests | 🔄 In progress |
| ⚡ Benchmarks | 🔄 In progress |
| ♿ Accessibility audit (light) | 🔄 In progress |
| ♿ Accessibility audit (dark) | 🔄 In progress |
All completed checks are green. The remaining jobs (browser tests, benchmarks, accessibility audits) are still running but are expected to pass as the commit touches no application logic.
✅ No quality concerns. This is a safe, self-contained housekeeping fix.
Generated by WolfStar Code Quality Reviewer · ◷
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [std-env](https://redirect.github.com/unjs/std-env) | [`^3.10.0` → `^4.0.0`](https://renovatebot.com/diffs/npm/std-env/3.10.0/4.0.0) |  |  | --- > [!WARNING] > Some dependencies could not be looked up. Check the [Dependency Dashboard](../issues/83) for more information. --- ### Release Notes <details> <summary>unjs/std-env (std-env)</summary> ### [`v4.0.0`](https://redirect.github.com/unjs/std-env/blob/HEAD/CHANGELOG.md#v400-rc1) [Compare Source](https://redirect.github.com/unjs/std-env/compare/v3.10.0...v4.0.0) [compare changes](https://redirect.github.com/unjs/std-env/compare/v3.10.0...v4.0.0-rc.1) ##### 🚀 Enhancements - Agent detection ([#​179](https://redirect.github.com/unjs/std-env/pull/179)) - Check `MODE` variable for `isDevelopment` and `isProduction` flags ([#​114](https://redirect.github.com/unjs/std-env/pull/114)) ##### 🔥 Performance - No need to pass env ref ([44bab37](https://redirect.github.com/unjs/std-env/commit/44bab37)) ##### 🩹 Fixes - Jenkins detection ([#​171](https://redirect.github.com/unjs/std-env/pull/171)) ##### 💅 Refactors -⚠️ Remove env and process shims ([d4de8a4](https://redirect.github.com/unjs/std-env/commit/d4de8a4)) -⚠️ Improve codebase ([653b127](https://redirect.github.com/unjs/std-env/commit/653b127)) - Default `nodeEnv` to `undefined` ([c78098a](https://redirect.github.com/unjs/std-env/commit/c78098a)) - Remove `toBoolean` util ([23bfb8f](https://redirect.github.com/unjs/std-env/commit/23bfb8f)) ##### 📦 Build -⚠️ Esm-only dist ([d31cca3](https://redirect.github.com/unjs/std-env/commit/d31cca3)) ##### 🏡 Chore - Migrate to oxlint, oxfmt and tsgo ([3de70cf](https://redirect.github.com/unjs/std-env/commit/3de70cf)) - Update lockfile ([c6e909c](https://redirect.github.com/unjs/std-env/commit/c6e909c)) - Update ([06201cc](https://redirect.github.com/unjs/std-env/commit/06201cc)) - Apply automated updates ([8f0d79f](https://redirect.github.com/unjs/std-env/commit/8f0d79f)) - Update deps ([8168254](https://redirect.github.com/unjs/std-env/commit/8168254)) - Prepare for v4 rc ([58118f5](https://redirect.github.com/unjs/std-env/commit/58118f5)) #####⚠️ Breaking Changes -⚠️ Remove env and process shims ([d4de8a4](https://redirect.github.com/unjs/std-env/commit/d4de8a4)) -⚠️ Improve codebase ([653b127](https://redirect.github.com/unjs/std-env/commit/653b127)) -⚠️ Esm-only dist ([d31cca3](https://redirect.github.com/unjs/std-env/commit/d31cca3)) ##### ❤️ Contributors - Pooya Parsa ([@​pi0](https://redirect.github.com/pi0)) - Mark R. Florkowski ([@​markflorkowski](https://redirect.github.com/markflorkowski)) - Gustavo Perdomo <gperdomor@gmail.com> </details> --- ### Configuration 📅 **Schedule**: Branch creation - "on Monday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/wolfstar-project/wolfstar.rocks). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My45NC4xIiwidXBkYXRlZEluVmVyIjoiNDMuOTQuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiY2hvcmUiLCJkZXBlbmRlbmNpZXMiXX0=--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: RedStar <redstar071@proton.me> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated code ownership configuration. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: LoryPelli <87276663+lorypelli@users.noreply.github.com>
…de quality configurations (#123) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated code quality workflow configuration to prevent no-op outputs from being reported as issues in pull requests. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
🤖 Code Quality Overview —
|
| Check | Status |
|---|---|
| 🔠 Lint | ✅ Passed |
| 🧪 Unit tests | ✅ Passed |
| 🧪 Component tests | ✅ Passed |
| 🖥️ Browser tests | ✅ Passed |
| 🧹 Unused code check | ✅ Passed |
| ♿ Accessibility audit (light) | ✅ Passed |
| ♿ Accessibility audit (dark) | 🔄 In progress |
| ⚡ Benchmarks | 🔄 In progress |
| 🔒 GitGuardian | ✅ Passed |
| 📊 Codecov | ✅ Passed |
Change Assessment
The PR template is well-structured and follows good contribution hygiene practices. A couple of minor observations:
- Duplicate context slots: The
### 🧭 Contextsection contains two adjacent comment placeholders (<!-- Brief background … -->and<!-- High-level summary … -->). Consider merging these into one prompt to avoid ambiguity about what goes where. - Checklist placement: The pre-submission checklist is wrapped in an HTML comment block, meaning it will not render as interactive checkboxes. This is a reasonable design choice if the intent is guidance-only, but contributors won't be able to tick items. If you want trackable checkboxes, move the list outside the comment block.
No bugs, security concerns, or accessibility issues — this is a safe, low-risk change. ✅
Generated by WolfStar Code Quality Reviewer · ◷
There was a problem hiding this comment.
Code quality review — v0.2.0 release PR (latest update)
CI status
| Check | Status |
|---|---|
| 🔠 Lint | ✅ passed |
| 🧪 Unit tests | ✅ passed |
| 🧪 Component tests | ✅ passed |
| 🧹 Unused code (Knip) | ✅ passed |
| 🔒 GitGuardian | ✅ passed |
| 📊 Codecov (patch + project) | ✅ passed |
| 🌐 Netlify deploy preview | ✅ ready |
| 🖥️ Browser tests | ⏳ in progress |
| ♿ Accessibility audit (light) | ⏳ in progress |
| ♿ Accessibility audit (dark) | ⏳ in progress |
| ⚡ Benchmarks | ⏳ in progress |
New commit since last review
The only change since the previous review is e9f0471c — feat: add pull request template (.github/PULL_REQUEST_TEMPLATE.md, +28 lines, documentation-only).
The template is well-structured:
- Linked issue, context, and description sections
- A thoughtful AI-usage note encouraging contributors to write in their own voice
- A contributor checklist (conventional commits, documentation, tests, etc.)
No code changes, no regressions. ✅
Open items from prior reviews (carry-over)
The following non-blocking items were flagged in earlier reviews and remain unaddressed — worth tracking as follow-up work before or shortly after the v0.2.0 release:
fix(cards)— label/action mismatch: the reload button now navigates to/loginbut still uses thearrow-pathicon and "Reload Page" label. This is a WCAG 2.5.3 issue; suggested fix:icon="heroicons:arrow-right-on-rectangle"+ label"Log In".createApiComposable.ts—isStalecheck runs at setup time, not insideonMounted, so SWR refreshes can be silently skipped on data that goes stale after hydration.PermissionNodeManager.ts—#refreshPromiseis never cleared after resolution, adding a microtask overhead on every subsequentrun()call.server/utils/discord.ts—?? falseafterArray.includes()is redundant (always boolean).useUser.ts—?? nullfallback is inconsistent with theRef<string | undefined>return type.
None of these are blockers for the release. The PR is in good shape pending the browser tests, accessibility audits, and benchmarks completing green.
Generated by WolfStar Code Quality Reviewer for PR #112
Generated by WolfStar Code Quality Reviewer for issue #112
Fixes WOLFSTAR-ROCKS-45 When a Discord API guild member response lacks the permissions field, BigInt(undefined) throws TypeError. Fix by marking permissions as optional in the type assertion and defaulting to "0" (no permissions) when absent. Agent-Logs-Url: https://github.com/wolfstar-project/wolfstar.rocks/sessions/f906e866-c8c7-4fb8-b14d-f685843f3cef Co-authored-by: RedStar071 <76824516+RedStar071@users.noreply.github.com>
…ns absent
The bot-token GET /guilds/{id}/members/{id} endpoint never returns a
permissions field (only interaction-attached members and the user-token
GET /users/@me/guilds/{id}/member do). Thread the pre-computed
hasManageGuild from oauthGuild.permissions through manage() and isAdmin()
so it is used as the correct fallback instead of hard-coding false.
Fixes WOLFSTAR-ROCKS-45
Agent-Logs-Url: https://github.com/wolfstar-project/wolfstar.rocks/sessions/aeb08cef-6762-4b9b-b020-f862b543de3b
Co-authored-by: RedStar071 <76824516+RedStar071@users.noreply.github.com>
…issions functions
…n/getManageable - Remove early Administrator return from computePermissions; return full bitfield - isAdmin: check Administrator || ManageGuild so admins are never denied access - isAdmin: fix cast to permissions?: string so type matches runtime reality - getManageable: replace useApi().users.getGuildMember (wrong endpoint/token) with getMember(guild.id, userId) bot-token helper Agent-Logs-Url: https://github.com/wolfstar-project/wolfstar.rocks/sessions/579a9929-ecc6-4ba2-9c5a-06fdad52f33f Co-authored-by: RedStar071 <76824516+RedStar071@users.noreply.github.com>
`BigInt(undefined)` throws at runtime when the Discord API returns a
guild member without a `permissions` field, causing the entire
`/api/users` guild transformation to fail for affected users.
## Root Cause
The `permissions` field is absent by Discord API design on members
fetched via the bot-token endpoint `GET
/guilds/{guild.id}/members/{user.id}`. It is only present on
interaction-attached member objects and `GET
/users/@me/guilds/{guild.id}/member` (user-token). Since `getManageable`
uses the bot-token fetch internally, `permissions` is never available
there.
## Changes
- **`server/utils/discord.ts`** — `computePermissions`: removed the
early `Administrator`-only return; the function now always returns the
full accumulated permission bitfield from all roles.
- **`server/utils/discord.ts`** — `isAdmin`: fixed `cast<{ permissions?:
string }>` (was `string`, hiding the optional field); when `permissions`
is absent, falls back to computing permissions from the member's roles
via `computePermissions`; explicitly checks `Administrator ||
ManageGuild` so users with the Administrator permission are never
falsely denied.
- **`server/utils/discord.ts`** — `getManageable`: replaced
`useApi().users.getGuildMember(guild.id)` (wrong endpoint — user-token
route, missing `userId`) with `getMember(guild.id, userId)`, the
existing cached bot-token helper.
```typescript
// Before — crashes when permissions is absent
const memberRolePermissions = BigInt(cast<{ permissions: string }>(member).permissions);
// After — falls back to role-based computation when permissions is absent;
// Administrator correctly implies ManageGuild access
const rawPermissions = cast<{ permissions?: string }>(member).permissions;
const memberRolePermissions =
rawPermissions !== undefined
? BigInt(rawPermissions)
: computePermissions(member.roles, guild.roles);
return (
PermissionsBits.has(memberRolePermissions, PermissionFlagsBits.Administrator) ||
PermissionsBits.has(memberRolePermissions, PermissionFlagsBits.ManageGuild)
);
```
The `cast<T>()` utility is a TypeScript-only type assertion with no
runtime validation, so the `string` annotation was silently masking an
optional field that the Discord API does not populate for bot-token
guild member fetches.
There was a problem hiding this comment.
Code quality review — v0.2.0 release PR (latest update)
CI status
| Check | Status |
|---|---|
| 🔠 Lint | ✅ passed |
| 🧪 Unit tests | ✅ passed |
| 🧪 Component tests | ✅ passed |
| 🧹 Unused code (Knip) | ✅ passed |
| ⚡ Benchmarks | ✅ passed |
| 🔒 GitGuardian | ✅ passed |
| 📊 Codecov | ✅ passed |
| ⚡ CodSpeed | ✅ passed |
| 🌐 Netlify deploy preview | ✅ ready |
| 🖥️ Browser tests | ⏳ in progress |
| ♿ Accessibility audit (light) | ⏳ in progress |
| ♿ Accessibility audit (dark) | ⏳ in progress |
Browser tests and both accessibility audits are still running. Wait for these to complete before merging.
New changes since last review
Three fix(discord) commits addressing the permission system (6ee225ec → 50b058e2 → 8d43c3ed):
What changed in server/utils/discord.ts:
computePermissions: early-Administrator short-circuit removed; now always returns full computed bitfield. ✅isAdmin: correctly checksAdministrator || ManageGuildand falls back tocomputePermissions(member.roles, guild.roles)whenrawPermissionsis absent (bot-token members). ✅getManageable: replacesuseApi().users.getGuildMember(wrong token context) withgetMember(guild.id, userId)bot-token helper; useshasManageGuildfrom OAuth data when bot-token fetch fails. ✅
These are all correct fixes for a subtle Discord API behaviour. The permission logic is now sound.
One micro-nit in the new code (non-blocking): member.roles.map((r) => r) in isAdmin is an identity map — member.roles can be passed directly to hasAtLeastOneKeyInMap since it's already an iterable.
Carry-over items from prior reviews (still open)
These were flagged in earlier reviews and remain unaddressed. None are release-blockers, but worth tracking as follow-up issues:
-
PermissionNodeManager.tsline 33–38 —#refreshPromiseis only cleared on error (} catch { this.#refreshPromise = null }), not on success. Every subsequentrun()call re-awaits the same already-resolved promise (extra microtask). Addingthis.#refreshPromise = nullin afinallyblock would eliminate this overhead. -
createApiComposable.ts/useUser.ts— TheisStaleguard (if (import.meta.client && asyncData.data.value?.isStale)) is evaluated at composable setup time, outsideonMounted. Data that is fresh at setup but becomes stale before mount will silently skip the SWR refresh. -
server/utils/discord.tslines 165, 169 —data.features.includes(GuildFeature.Partnered) ?? false/...Verified ?? false:Array.includes()always returnsboolean, so?? falseis redundant. -
fix(cards)— label/action mismatch — the reload button navigates to/loginbut still carries thearrow-pathicon and "Reload Page" label. WCAG 2.5.3 issue; suggested fix:icon="heroicons:arrow-right-on-rectangle"+ label"Log In".
Generated by WolfStar Code Quality Reviewer for PR #112
Note
🔒 Integrity filter blocked 6 items
The following items were blocked because they don't meet the GitHub integrity level.
- 6b57ea3
list_commits: has lower integrity than agent requires. The agent cannot read data with integrity below "approved". - 962e5f0
list_commits: has lower integrity than agent requires. The agent cannot read data with integrity below "approved". - 532bc08
list_commits: has lower integrity than agent requires. The agent cannot read data with integrity below "approved". - e9f0471
list_commits: has lower integrity than agent requires. The agent cannot read data with integrity below "approved". - fcb21a3
list_commits: has lower integrity than agent requires. The agent cannot read data with integrity below "approved". - 4e2e463
list_commits: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
To allow these resources, lower min-integrity in your GitHub frontmatter:
tools:
github:
min-integrity: approved # merged | approved | unapproved | noneGenerated by WolfStar Code Quality Reviewer for issue #112
🔍 Code Quality Review —
|
| Job | Result |
|---|---|
| 🔠 Lint project | ✅ success |
| 🧪 Unit tests | ✅ success |
| 🧪 Component tests | ✅ success |
| 🖥️ Browser tests | ✅ success |
| ♿ Accessibility audit (light) | ✅ success |
| ♿ Accessibility audit (dark) | ✅ success |
| 🧹 Unused code check | ✅ success |
| ⚡ Benchmarks | ✅ success |
✅ What's good
- Bug fix is correct.
BigInt(undefined)was a guaranteed runtime crash on every bot-token guild member fetch. Thepermissions !== undefinedguard withcomputePermissionsfallback is the right approach. - Wrong endpoint fixed.
useApi().users.getGuildMember(guild.id)was using the user-token route and missinguserId; the replacementgetMember(guild.id, userId)correctly uses the cached bot-token helper. computePermissionsis clean. Iterating all guild roles and accumulating the bitfield via|=is correct and handles the@everyonerole properly.Administrator || ManageGuildcheck is correct. AnAdministratorimplies all permissions includingManageGuild, so checking both explicitly (rather than relying on implication) is safe and readable.
🔶 Observations worth addressing
1. Unnecessary identity map in isAdmin
// Current
member.roles.map((r) => r)
// Simpler
member.rolesArray.prototype.map((r) => r) creates a new array copy with identical values. The hasAtLeastOneKeyInMap second argument just needs an iterable of strings; passing member.roles directly is equivalent and avoids the allocation.
2. No tests for the new fallback path
isAdmin (and by extension getManageable) is on the critical path for access control. The two new code paths — permissions absent → computePermissions fallback, and getMember throws → fall back to hasManageGuild — are not covered by tests. Given this is a security-sensitive function, adding unit tests for both branches would be valuable:
// Example cases to cover:
// - member with permissions field present → BigInt path
// - member with permissions field absent → computePermissions path
// - member is guild owner
// - getMember throws → graceful fallback to oauthGuild permissions3. Silent error swallow in getManageable
const member = await getMember(guild.id, userId).catch(() => undefined);Transient Discord API errors or 5xx responses will be silently discarded, falling back to hasManageGuild. While this is better than crashing, it means Sentry won't capture real API failures here. Consider capturing non-"unknown member" errors:
const member = await getMember(guild.id, userId).catch((err) => {
if (err?.code !== 10_007) Sentry.captureException(err);
return undefined;
});4. cast is redundant on APIGuildMember
APIGuildMember from discord-api-types/v10 already defines permissions?: string as an optional field. The cast<{ permissions?: string }>(member) call is therefore doing nothing — member.permissions is already typed correctly and can be accessed directly without the cast utility. This is minor, but cast should be reserved for when you genuinely need to override the type system.
Summary
The core fix is correct and all CI checks are green. The four observations above are non-blocking: items 1 and 4 are style/clarity, item 2 is a test-coverage gap on security-sensitive logic worth addressing in a follow-up, and item 3 is an observability improvement for production debugging.
Generated by WolfStar Code Quality Reviewer · ◷
✅ Contribution Guidelines CheckThis automated release PR meets the repository's contribution guidelines:
The
|
🔍 Code Quality Overview —
|
| File | Type | Description |
|---|---|---|
shelve.config.json |
➕ Added | Shelve project configuration (project name + team slug) |
package.json |
✏️ Modified | Added @shelve/cli ^4.1.7 to devDependencies |
pnpm-lock.yaml |
✏️ Modified | Lockfile updated for new dependency |
This is a minimal, focused change: it bootstraps Shelve — an environment-variable management CLI — for this project.
CI Status
| Workflow | Status |
|---|---|
ci (push) |
🔄 In progress |
ci (PR) |
🔄 In progress |
release-pr |
✅ Passed |
CI is still running. No prior failures are visible for this set of changes.
Observations
-
shelve.config.jsoncommitted to the repo — The file only contains the project name (wolfstar.rocks) and team slug (wolfstar-team), which are non-sensitive identifiers. No tokens or secrets are present. ✅ -
$schemapoints to an external URL —https://raw.githubusercontent.com/HugoRCD/shelve/main/packages/types/schema.jsonis a GitHub raw URL that could drift if the upstream schema changes. Consider pinning to a specific release tag or SHA if schema stability is important. -
@shelve/cliin devDependencies — Correct placement; CLI tools should not be independencies. Thepostinstallscript already runsvp config, so if Shelve pull/push is hooked there, ensure CI environments haveSHELVE_TOKEN(or equivalent) set to avoid install-time failures. -
No application code changed — This commit carries zero risk of UI/UX regression, accessibility issues, or server-side bugs.
ℹ️ No blocking issues found. This change is safe to proceed once CI completes.
Generated by WolfStar Code Quality Reviewer · ◷
There was a problem hiding this comment.
Code quality review — v0.2.0 release PR
CI status
| Check | Status |
|---|---|
| 🔠 Lint | ✅ passed |
| 🧪 Unit tests | ✅ passed |
| 🧪 Component tests | ✅ passed |
| 🧹 Unused code (Knip) | ❌ failing |
| 🔒 GitGuardian | ✅ passed |
| 🖥️ Browser tests | 🔄 in progress |
| ♿ Accessibility audit (light/dark) | 🔄 in progress |
| ⚡ Benchmarks | 🔄 in progress |
Required fix
The 🧹 Unused code check is failing due to one issue:
@shelve/cliis flagged as an unused devDependency (package.json:119). This package is CLI-only (invoked asshelve pull/push) and is not imported anywhere — Knip can't detect CLI-only tools automatically.
Fix: add one line to knip.ts > ignoreDependencies:
/** Shelve CLI — used for secrets management via `shelve pull/push`, not imported directly */
"`@shelve/cli`",Non-blocking cleanup (optional follow-up)
Knip reports 7 configuration hints about stale entries in knip.ts (ignoreDependencies / ignoreUnresolved). These don't cause the CI failure on their own but indicate drift worth cleaning up in a follow-up commit.
This is the only blocking issue. All code-quality changes in this release (evlog migration, createApiComposable, OAuth hardening, vite-plus migration) look well-structured and consistent with the repo's patterns.
Generated by WolfStar Code Quality Reviewer for issue #112
There was a problem hiding this comment.
Code Review — v0.2.0 Release PR
CI status at review time
| Check | Status |
|---|---|
| 🔠 Lint | ✅ Passed |
| 🧪 Unit tests | ✅ Passed |
| 🧪 Component tests | ✅ Passed |
| ⚡ Benchmarks | ✅ Passed |
| 🧹 Unused code | ✅ Passed |
| 🔒 GitGuardian | ✅ Passed |
| 🌐 Netlify preview | ✅ Ready (deploy-preview-112) |
| 🖥️ Browser tests | 🔄 In progress |
| ♿ Accessibility audits (light + dark) | 🔄 In progress |
Overall the quality baseline is strong. All static checks pass. I reviewed the key new runtime code (sentry-metrics.ts, discord.ts, wrappedEventHandler.ts, ApiTransformers.ts, createApiComposable.ts, oauth-state.ts) and left inline notes on five non-blocking observations.
Summary of inline comments
-
Snowflake ID normalization gap (
sentry-metrics.ts:14) — the{20,}quantifier misses 17–19 digit Discord IDs, so route metrics likeGET /api/guilds/:id/settingswould fan out per guild in Sentry. Worth patching before substantial production traffic hits the endpoint. -
credentials: "include"on server-to-server fetch (discord.ts) — harmless today but misleading in a Node.js context; remove or replace withcredentials: "omit". -
Redundant identity
.map((r) => r)(discord.ts) — minor allocation with no functional purpose; passmember.rolesdirectly. -
Unreachable
?? []after.map()(ApiTransformers.ts) — dead guard; the defensive check should be(arr ?? []).map(...)notarr.map(...) ?? []. -
any | Error | H3Errorunion (wrappedEventHandler.ts) —anysubsumes the others; preferError | H3Error | unknown.
Submitting as COMMENT rather than APPROVE since browser tests and accessibility audits are still running — please confirm they pass before merging.
Generated by WolfStar Code Quality Reviewer for issue #112
Unable to generate the performance reportThere was an internal error while processing the run's data. We're working on fixing the issue. Feel free to contact us on Discord or at support@codspeed.io if the issue persists. |
This PR will deploy the following changes to production (
wolfstar.rocks).Next version:
v0.2.0(current:v0.1.0)Features
1b225d98)e9f0471c)02ccf5ff)5a5000ce)136e9f48)a50f1b1d)2124b5fa)a082e97b)28d3348f)c6bcaff2)Fixes
ff79dfd6)8d43c3ed)962e5f02)532bc087)50b058e2)6ee225ec)827e4950)6bdfbfb8)ce767b74)8132ebd0)760df4fa)34f933b1)1d19bd1d)d5ad6eb0)61503796)434a16c0)446859c2)5fbdca85)8725751c)e05e1326)ece11d86)50c0a805)18b6b84a)58f60be2)3891eb0e)fcf6bfda)bbe73c59)false(5375da9e)ab9a544b)8b9d11d4)417cc237)baf3b054)004aedb1)add18b83)17886de5)1b61d487)5158f0bd)Other Changes
fcb21a3b)4e2e463c)4e8d3b24)7904ea02)3ccf7886)32e5d27d)b25f1d9c)3356bca8)19931fd1)449d6d92)ac2b6174)18f445ee)f8fbfd62)48a018fe)92dde526)951936a6)4ab48318)1efd1b16)845d3289)9d639d63)ec8a94f0)6f9a33af)31b75380)1c6ee898)ceb40a0e)91bb909a)d08e2e04)e2b66478)1d085773)16933906)4177bc54)eec99761)5272420f)136df097)38f4d865)a514341d)4f0df00b)5fee2495)1dc84cfb)e72ab852)b9d60c54)3b8dfaae)0223ed27)2375a0f3)ecaf8d17)572f2124)f811722e)c97307c0)8bc6a133)1f6af1ae)d323a532)47bc2734)31aceb72)53d20d25)0bc6d3e9)eb4b3c96)45749d40)consola(056662ff)22583f3a)0544bcb6)5d05f70c)6982c8cd)055e5981)0254d85b)47479196)0527396b)a66f3e5a)95a5cd69)fb7c7612)de064a17)714edc65)60c9d0b2)a0d49605)b3cc0458)40403287)af008693)cc756897)04594280)1435739c)28bff2c2)d2d4b388)caa501f6)57584dec)82a45473)03e92bcb)738ad8b4)17eecc6d)Uncategorized
f40f8e4a)abe7a058)dd606c3b)46fe3d68)6357c182)e8ee404d)9a497fd8)777b1480)6aef6279)de71a2ed)ece8fc0d)e2b3fdc4)0d5b7fd8)4350700f)