Skip to content

Remove role based perms, fetch cosmetics.json from api#1640

Merged
evanpelle merged 2 commits intomainfrom
evan-fetch-cosmetics
Aug 4, 2025
Merged

Remove role based perms, fetch cosmetics.json from api#1640
evanpelle merged 2 commits intomainfrom
evan-fetch-cosmetics

Conversation

@evanpelle
Copy link
Collaborator

@evanpelle evanpelle commented Jul 30, 2025

Description:

  • Fetch cosmetics.json from api
  • Remove all role based perms, we are only using flares now
  • Created Priviledge refresher which periodically polls /cosmetics.json endpoint.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced
  • I have read and accepted the CLA agreement (only required once).

Please put your Discord username so you can be contacted if a bug or regression is found:

evan

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 30, 2025

Warning

Rate limit exceeded

@evanpelle has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 57 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 423b602 and 1103ab7.

📒 Files selected for processing (10)
  • .gitignore (1 hunks)
  • resources/cosmetics/roles.txt (0 hunks)
  • src/client/Cosmetics.ts (1 hunks)
  • src/client/TerritoryPatternsModal.ts (6 hunks)
  • src/core/CosmeticSchemas.ts (1 hunks)
  • src/core/CustomFlag.ts (4 hunks)
  • src/server/Privilege.ts (6 hunks)
  • src/server/PrivilegeRefresher.ts (1 hunks)
  • src/server/Worker.ts (5 hunks)
  • tests/server/Privilege.customFlag.test.ts (1 hunks)

Walkthrough

This change removes all role-based logic and static role data from the cosmetics and privilege-checking system. Cosmetics data and access checks now use only flare-based permissions, with cosmetics fetched dynamically from an API. All references to roles, role groups, and static product imports are eliminated across client, server, and schema code.

Changes

Cohort / File(s) Change Summary
Static Role Data Removal
resources/cosmetics/roles.txt
Deleted file containing static role name-to-ID mappings.
Cosmetics Client Refactor
src/client/Cosmetics.ts, src/client/TerritoryPatternsModal.ts
Removed all role and static product logic; now fetches cosmetics from API and checks access via flares only. Updated UI logic to use product field for locked/purchasable state.
Cosmetic Schemas Update
src/core/CosmeticSchemas.ts
Removed all role group fields; added new schemas for product and pattern; all permissions now flare-based.
Custom Flag Rendering
src/core/CustomFlag.ts
Refactored to require cosmetics data as a parameter; removed static cosmetics import.
Privilege Checking Overhaul
src/server/Privilege.ts, src/server/PrivilegeRefresher.ts
Replaced role-based privilege logic with flare-based checks. Introduced PrivilegeRefresher for dynamic privilege updates. Added fail-open no-op checker.
Worker Startup and Privilege Use
src/server/Worker.ts
Replaced static privilege checker with PrivilegeRefresher; removed role-based checks in message handling.
Privilege Tests Update
tests/server/Privilege.customFlag.test.ts
Updated tests to use flare-only permissions; removed all role group test logic.
Git Ignore Update
.gitignore
Added CLAUDE.md to ignored files.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Frontend
    participant API
    participant Server
    participant PrivilegeRefresher

    User->>Frontend: Requests cosmetics
    Frontend->>API: GET /cosmetics.json
    API-->>Frontend: Cosmetics JSON (with flares, no roles)
    Frontend->>Frontend: Filter patterns by user flares
    User->>Frontend: Attempts to use a pattern or flag
    Frontend->>Server: Request with pattern/flag and user flares
    Server->>PrivilegeRefresher: Get current PrivilegeChecker
    PrivilegeRefresher->>Server: Return checker (flare-based)
    Server->>Server: Check permission (flares only)
    Server-->>Frontend: Allow or deny
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related issues

Possibly related PRs

Suggested labels

Premium, Feature - Frontend

Suggested reviewers

  • scottanderson

Poem

Out with the roles, the old guard is gone,
Now only flares decide what patterns live on.
Cosmetics fetch fresh, no more static in sight,
Privileges checked by flares, not roles in the night.
The code is now cleaner, the logic more fair—
Let patterns and flags sparkle with flair! ✨


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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@evanpelle evanpelle changed the base branch from main to v24 July 30, 2025 21:14
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (5)
src/core/CustomFlag.ts (1)

36-37: Remove unnecessary mask check

The mask variable is always a non-empty string, so the condition if (!mask) continue; will never be true.

     const mask = `/flags/custom/${layerName}.svg`;
-    if (!mask) continue;
src/client/Cosmetics.ts (1)

59-69: Consider caching cosmetics data

The getCosmetics function fetches data from the API on every call. Since cosmetics data likely doesn't change frequently, consider caching the result to improve performance.

Would you like me to implement a simple caching mechanism with a TTL (time-to-live) for the cosmetics data?

src/core/CosmeticSchemas.ts (1)

16-16: Update outdated comment

The comment references resources/cosmetics/cosmetics.json which has been removed. Update it to reflect that cosmetics are now fetched from an API endpoint.

-// Schema for resources/cosmetics/cosmetics.json
+// Schema for cosmetics data fetched from API
src/server/Worker.ts (1)

47-47: Use URL constructor for robust path joining

Direct string concatenation assumes jwtIssuer() has no trailing slash. Use URL constructor for proper path handling.

-  const endpoint = config.jwtIssuer() + "/cosmetics.json";
+  const endpoint = new URL("/cosmetics.json", config.jwtIssuer()).toString();
src/server/Privilege.ts (1)

130-144: Prefix unused parameters with underscore

The NoOpPrivilegeChecker correctly implements the bypass behavior, but unused parameters should follow TypeScript naming conventions.

 export class NoOpPrivilegeChecker implements PrivilegeChecker {
   isPatternAllowed(
-    base64: string,
-    flares: readonly string[] | undefined,
+    _base64: string,
+    _flares: readonly string[] | undefined,
   ): true | "restricted" | "unlisted" | "invalid" {
     return true;
   }

   isCustomFlagAllowed(
-    flag: string,
-    flares: readonly string[] | undefined,
+    _flag: string,
+    _flares: readonly string[] | undefined,
   ): true | "restricted" | "invalid" {
     return true;
   }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c548469 and d756506.

📒 Files selected for processing (14)
  • resources/cosmetics/cosmetics.json (0 hunks)
  • resources/cosmetics/roles.txt (0 hunks)
  • src/client/Cosmetics.ts (2 hunks)
  • src/client/TerritoryPatternsModal.ts (12 hunks)
  • src/client/graphics/GameRenderer.ts (1 hunks)
  • src/client/graphics/layers/ChatModal.ts (2 hunks)
  • src/client/graphics/layers/EmojiTable.ts (2 hunks)
  • src/client/graphics/layers/PlayerPanel.ts (2 hunks)
  • src/client/graphics/layers/RadialMenu.ts (2 hunks)
  • src/core/CosmeticSchemas.ts (1 hunks)
  • src/core/CustomFlag.ts (1 hunks)
  • src/server/Privilege.ts (6 hunks)
  • src/server/Worker.ts (5 hunks)
  • tests/server/Privilege.customFlag.test.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • resources/cosmetics/cosmetics.json
  • resources/cosmetics/roles.txt
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: andrewNiziolek
PR: openfrontio/OpenFrontIO#1007
File: resources/lang/de.json:115-115
Timestamp: 2025-06-02T14:27:37.609Z
Learning: For OpenFrontIO project: When localization keys are renamed in language JSON files, the maintainers separate technical changes from translation content updates. They wait for community translators to update the actual translation values rather than attempting to translate in the same PR. This allows technical changes to proceed while ensuring accurate translations from native speakers.
Learnt from: scottanderson
PR: openfrontio/OpenFrontIO#786
File: src/server/Client.ts:16-18
Timestamp: 2025-06-12T23:22:55.328Z
Learning: Undefined values for `roles`, `flares`, and `pattern` are intentionally used to mark an unauthenticated client, whereas empty arrays/strings indicate an authenticated user with no roles or flares. Avoid suggesting non-optional types for these fields.
src/client/graphics/GameRenderer.ts (1)

Learnt from: VariableVince
PR: #1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

src/client/graphics/layers/ChatModal.ts (1)

Learnt from: VariableVince
PR: #1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

src/client/graphics/layers/PlayerPanel.ts (1)

Learnt from: VariableVince
PR: #1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

src/client/graphics/layers/EmojiTable.ts (1)

Learnt from: VariableVince
PR: #1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

src/client/graphics/layers/RadialMenu.ts (2)

Learnt from: VariableVince
PR: #1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Learnt from: VariableVince
PR: #1192
File: src/client/graphics/layers/RadialMenuElements.ts:312-314
Timestamp: 2025-06-16T03:03:59.778Z
Learning: In RadialMenuElements.ts, when fixing undefined params errors in subMenu functions, use explicit checks like if (params === undefined || params.selected === null) rather than optional chaining, as it makes the intent clearer and matches the specific error scenarios where params can be undefined during spawn phase operations.

tests/server/Privilege.customFlag.test.ts (1)

Learnt from: Aotumuri
PR: #709
File: src/client/Main.ts:276-296
Timestamp: 2025-05-16T12:06:01.732Z
Learning: In the OpenFrontIO codebase, checkPermission() function's return values need to be handled with defensive checks using Array.isArray(), even though its TypeScript signature indicates it returns arrays. Removing these checks breaks functionality.

src/core/CustomFlag.ts (1)

Learnt from: Aotumuri
PR: #786
File: src/core/Util.ts:4-4
Timestamp: 2025-06-07T13:15:55.439Z
Learning: In the OpenFrontIO codebase, JSON files should be imported using standard import syntax without import attributes, as the TypeScript configuration supports resolveJsonModule and the codebase already uses this pattern successfully in files like src/client/Cosmetic.ts.

src/client/Cosmetics.ts (1)

Learnt from: Aotumuri
PR: #786
File: src/core/Util.ts:4-4
Timestamp: 2025-06-07T13:15:55.439Z
Learning: In the OpenFrontIO codebase, JSON files should be imported using standard import syntax without import attributes, as the TypeScript configuration supports resolveJsonModule and the codebase already uses this pattern successfully in files like src/client/Cosmetic.ts.

src/server/Worker.ts (5)

Learnt from: scottanderson
PR: #1161
File: src/server/jwt.ts:0-0
Timestamp: 2025-06-19T19:31:29.475Z
Learning: In Zod v4, the correct import is import { z } from "zod/v4" and z.prettifyError(error) is a built-in utility method for formatting ZodError into readable, multi-line strings. This is different from Zod v3.x which required different error handling approaches.

Learnt from: Aotumuri
PR: #786
File: src/core/Util.ts:4-4
Timestamp: 2025-06-07T13:15:55.439Z
Learning: In the OpenFrontIO codebase, JSON files should be imported using standard import syntax without import attributes, as the TypeScript configuration supports resolveJsonModule and the codebase already uses this pattern successfully in files like src/client/Cosmetic.ts.

Learnt from: scottanderson
PR: #784
File: src/core/game/StatsImpl.ts:34-38
Timestamp: 2025-05-21T04:10:33.435Z
Learning: In the codebase, PlayerStats is defined as z.infer<typeof PlayerStatsSchema> where PlayerStatsSchema has .optional() applied at the object level, making PlayerStats a union type that already includes undefined (PlayerStats | undefined).

Learnt from: scottanderson
PR: #784
File: src/core/game/StatsImpl.ts:34-38
Timestamp: 2025-05-21T04:10:33.435Z
Learning: In the codebase, PlayerStats is defined as a type inferred from a Zod schema that is marked as optional, which means PlayerStats already includes undefined as a possible type (PlayerStats | undefined).

Learnt from: scottanderson
PR: #786
File: src/server/Client.ts:16-18
Timestamp: 2025-06-12T23:22:55.328Z
Learning: Undefined values for roles, flares, and pattern are intentionally used to mark an unauthenticated client, whereas empty arrays/strings indicate an authenticated user with no roles or flares. Avoid suggesting non-optional types for these fields.

src/client/TerritoryPatternsModal.ts (5)

Learnt from: scottanderson
PR: #786
File: src/client/TerritoryPatternsModal.ts:337-338
Timestamp: 2025-06-22T05:48:19.241Z
Learning: In src/client/TerritoryPatternsModal.ts, the bit shifting operators (<<) used in coordinate calculations with decoder.getScale() are intentional and should not be changed to multiplication. The user scottanderson confirmed this is functioning as intended.

Learnt from: VariableVince
PR: #1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Learnt from: scottanderson
PR: #786
File: src/client/TerritoryPatternsModal.ts:299-299
Timestamp: 2025-06-22T05:33:39.581Z
Learning: In the OpenFrontIO codebase, territory patterns used by PatternDecoder are curated by the development team and stored in a config file in source control, making them trusted data with minimal risk of malformation that would cause PatternDecoder to throw errors.

Learnt from: devalnor
PR: #1248
File: src/client/graphics/layers/TerritoryInfoLayer.ts:20-20
Timestamp: 2025-06-22T21:51:14.990Z
Learning: In TerritoryInfoLayer.ts, the highlightedTerritory field uses both null and undefined intentionally: undefined represents initial state or inactive layer (Ctrl released), while null represents active layer with no territory being highlighted at cursor position. This distinction is important for proper state change detection.

Learnt from: devalnor
PR: #1195
File: src/client/graphics/layers/AlertFrame.ts:18-18
Timestamp: 2025-06-20T20:11:00.965Z
Learning: In the OpenFrontIO codebase, UserSettings instances are created directly with new UserSettings() in each component that needs them. This pattern is used consistently across at least 12+ files including OptionsMenu, EventsDisplay, DarkModeButton, Main, UserSettingModal, UsernameInput, NameLayer, AlertFrame, UILayer, InputHandler, ClientGameRunner, and GameView. This is the established architectural pattern and should be followed for consistency.

src/core/CosmeticSchemas.ts (4)

Learnt from: Aotumuri
PR: #786
File: src/core/Util.ts:4-4
Timestamp: 2025-06-07T13:15:55.439Z
Learning: In the OpenFrontIO codebase, JSON files should be imported using standard import syntax without import attributes, as the TypeScript configuration supports resolveJsonModule and the codebase already uses this pattern successfully in files like src/client/Cosmetic.ts.

Learnt from: scottanderson
PR: #784
File: src/core/game/StatsImpl.ts:44-53
Timestamp: 2025-05-21T04:10:59.706Z
Learning: In Zod, when a schema has .optional() applied at the object level, the TypeScript type inferred using z.infer already includes undefined in the union type. Therefore, when using such a type as a return type annotation, there's no need to explicitly add | undefined.

Learnt from: scottanderson
PR: #1161
File: src/server/jwt.ts:0-0
Timestamp: 2025-06-19T19:31:29.475Z
Learning: In Zod v4, the correct import is import { z } from "zod/v4" and z.prettifyError(error) is a built-in utility method for formatting ZodError into readable, multi-line strings. This is different from Zod v3.x which required different error handling approaches.

Learnt from: scottanderson
PR: #784
File: src/core/game/StatsImpl.ts:34-38
Timestamp: 2025-05-21T04:10:33.435Z
Learning: In the codebase, PlayerStats is defined as z.infer<typeof PlayerStatsSchema> where PlayerStatsSchema has .optional() applied at the object level, making PlayerStats a union type that already includes undefined (PlayerStats | undefined).

src/server/Privilege.ts (3)

Learnt from: Aotumuri
PR: #709
File: src/client/Main.ts:276-296
Timestamp: 2025-05-16T12:06:01.732Z
Learning: In the OpenFrontIO codebase, checkPermission() function's return values need to be handled with defensive checks using Array.isArray(), even though its TypeScript signature indicates it returns arrays. Removing these checks breaks functionality.

Learnt from: scottanderson
PR: #786
File: src/server/Client.ts:16-18
Timestamp: 2025-06-12T23:22:55.328Z
Learning: Undefined values for roles, flares, and pattern are intentionally used to mark an unauthenticated client, whereas empty arrays/strings indicate an authenticated user with no roles or flares. Avoid suggesting non-optional types for these fields.

Learnt from: Ble4Ch
PR: #1063
File: src/core/configuration/PastelThemeDark.ts:53-53
Timestamp: 2025-06-10T09:56:44.473Z
Learning: In ColorAllocator class in src/core/configuration/Colors.ts, the correct method names are assignColor(id: string): Colord for general color assignment and assignTeamColor(team: Team): Colord for team-specific colors. There are no assignPlayerColor() or assignBotColor() methods.

🧬 Code Graph Analysis (8)
src/client/graphics/layers/ChatModal.ts (1)
src/client/InputHandler.ts (1)
  • CloseViewEvent (64-64)
src/client/graphics/layers/PlayerPanel.ts (1)
src/client/InputHandler.ts (1)
  • CloseViewEvent (64-64)
src/client/graphics/layers/EmojiTable.ts (1)
src/client/InputHandler.ts (1)
  • CloseViewEvent (64-64)
src/client/graphics/layers/RadialMenu.ts (1)
src/client/InputHandler.ts (1)
  • CloseViewEvent (64-64)
tests/server/Privilege.customFlag.test.ts (2)
src/core/CosmeticSchemas.ts (1)
  • Cosmetics (39-39)
src/server/Privilege.ts (1)
  • PrivilegeCheckerImpl (15-128)
src/client/Cosmetics.ts (3)
src/core/ApiSchemas.ts (1)
  • UserMeResponse (49-49)
src/core/CosmeticSchemas.ts (3)
  • Pattern (40-40)
  • Cosmetics (39-39)
  • CosmeticsSchema (17-38)
src/client/jwt.ts (1)
  • getApiBase (18-23)
src/client/TerritoryPatternsModal.ts (3)
src/core/CosmeticSchemas.ts (1)
  • Pattern (40-40)
src/client/Cosmetics.ts (1)
  • handlePurchase (27-57)
src/core/PatternDecoder.ts (1)
  • PatternDecoder (1-59)
src/core/CosmeticSchemas.ts (1)
src/core/Schemas.ts (2)
  • PatternSchema (225-225)
  • RequiredPatternSchema (207-224)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Deploy to openfront.dev
🔇 Additional comments (18)
src/client/graphics/layers/PlayerPanel.ts (2)

16-16: Clean import addition

Good addition of CloseViewEvent to the existing import from InputHandler.


170-172: Consistent event handling pattern

The CloseViewEvent listener follows the same clean pattern as the existing MouseUpEvent handler. This creates a unified way to close the player panel.

src/client/graphics/GameRenderer.ts (1)

186-186: Necessary initialization call

Good addition of initEventBus() call for ChatModal. This follows the same pattern as emojiTable and ensures the CloseViewEvent listener is properly registered during renderer setup.

src/client/graphics/layers/ChatModal.ts (2)

9-9: Clean import addition

Proper addition of CloseViewEvent import for the new event handling functionality.


176-182: Well-implemented event handler

The initEventBus() method is cleanly implemented with:

  • Proper conditional check using !this.hidden
  • Consistent arrow function syntax
  • Appropriate method call to close()

This creates a clean integration with the global close event system.

src/client/graphics/layers/EmojiTable.ts (2)

8-8: Clean import update

Good addition of CloseViewEvent to the existing import statement from InputHandler.


52-56: Consistent event handling pattern

The CloseViewEvent listener is cleanly integrated into the existing initEventBus() method. The conditional check with !this.hidden and call to hideTable() follows the same pattern as other UI components.

src/client/graphics/layers/RadialMenu.ts (2)

4-4: Clean import addition

Proper addition of CloseViewEvent import for the new event handling functionality.


106-108: Consistent event handling implementation

The CloseViewEvent listener is cleanly added to the existing init() method. The arrow function syntax and call to hideRadialMenu() follows the same pattern established in other UI components.

tests/server/Privilege.customFlag.test.ts (1)

1-96: Test updates correctly reflect the flare-based permission model

The tests have been properly updated to:

  • Use PrivilegeCheckerImpl class
  • Pass only flares (no roles)
  • Test flare-based access control
  • Include proper test coverage for various permission scenarios
src/core/CosmeticSchemas.ts (1)

1-42: Well-structured schema definitions

The schemas are properly defined using Zod v4 with:

  • Clear type definitions for Product, Pattern, and Cosmetics
  • Proper use of nullable and optional fields
  • Good separation of concerns
src/server/Privilege.ts (2)

4-13: Good interface extraction for testability

Extracting the PrivilegeChecker interface improves the design by enabling easier testing and alternative implementations.


74-76: Good defensive programming with optional chaining

The null-safe access for cosmetics.flag?.layers and cosmetics.flag?.color prevents runtime errors when flag data is absent.

src/client/TerritoryPatternsModal.ts (5)

104-116: Simplified tooltip logic for product-based patterns

The tooltip now correctly shows purchase prompt only for patterns with products, aligning with the new product-based access model.


158-171: Well-implemented purchase button integration

The conditional rendering of purchase buttons for locked patterns is clean and the non-null assertion is safe within the guarded block.


41-41: Good lifecycle management with isActive flag

The isActive flag prevents rendering when modal is closed, and proper cleanup of event listeners and ResizeObserver prevents memory leaks.

Also applies to: 223-223, 237-245


345-359: Excellent performance optimization with preview caching

The caching mechanism prevents redundant canvas operations for pattern previews. Module-level cache appropriately persists across component instances.

Also applies to: 397-399


327-342: Clean hover state management for purchase tooltips

The hover handlers correctly show tooltips only for patterns with products, providing good user feedback for purchasable items.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d756506 and e09c587.

📒 Files selected for processing (10)
  • resources/cosmetics/cosmetics.json (0 hunks)
  • resources/cosmetics/roles.txt (0 hunks)
  • src/client/Cosmetics.ts (2 hunks)
  • src/client/TerritoryPatternsModal.ts (5 hunks)
  • src/core/CosmeticSchemas.ts (1 hunks)
  • src/core/CustomFlag.ts (1 hunks)
  • src/server/Privilege.ts (6 hunks)
  • src/server/PrivilegeRefresher.ts (1 hunks)
  • src/server/Worker.ts (3 hunks)
  • tests/server/Privilege.customFlag.test.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • resources/cosmetics/roles.txt
  • resources/cosmetics/cosmetics.json
✅ Files skipped from review due to trivial changes (2)
  • src/core/CustomFlag.ts
  • src/server/Worker.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/client/Cosmetics.ts
  • tests/server/Privilege.customFlag.test.ts
  • src/core/CosmeticSchemas.ts
  • src/client/TerritoryPatternsModal.ts
  • src/server/Privilege.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: andrewNiziolek
PR: openfrontio/OpenFrontIO#1007
File: resources/lang/de.json:115-115
Timestamp: 2025-06-02T14:27:37.609Z
Learning: For OpenFrontIO project: When localization keys are renamed in language JSON files, the maintainers separate technical changes from translation content updates. They wait for community translators to update the actual translation values rather than attempting to translate in the same PR. This allows technical changes to proceed while ensuring accurate translations from native speakers.
Learnt from: scottanderson
PR: openfrontio/OpenFrontIO#786
File: src/server/Client.ts:16-18
Timestamp: 2025-06-12T23:22:55.328Z
Learning: Undefined values for `roles`, `flares`, and `pattern` are intentionally used to mark an unauthenticated client, whereas empty arrays/strings indicate an authenticated user with no roles or flares. Avoid suggesting non-optional types for these fields.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Deploy to openfront.dev
🔇 Additional comments (3)
src/server/PrivilegeRefresher.ts (3)

1-8: LGTM! Clear and descriptive imports.

The alias FailOpenPrivilegeChecker makes the security behavior more explicit than the original NoOpPrivilegeChecker name.


17-20: LGTM! Clean dependency injection.

Good use of TypeScript's automatic property assignment in constructor parameters.


32-37: LGTM! Proper fallback logic.

The fail-open behavior is correctly implemented. The null check will work properly once the field initialization is fixed.

@evanpelle evanpelle force-pushed the evan-fetch-cosmetics branch from e09c587 to 62ffc85 Compare July 30, 2025 22:37
@evanpelle evanpelle force-pushed the evan-fetch-cosmetics branch from 62ffc85 to c2cb1e7 Compare July 31, 2025 01:33
@evanpelle evanpelle changed the title Evan fetch cosmetics fetch cosmetics.json Jul 31, 2025
@evanpelle evanpelle force-pushed the evan-fetch-cosmetics branch from c2cb1e7 to 22c611e Compare July 31, 2025 15:43
@evanpelle evanpelle added this to the v24 milestone Aug 1, 2025
@evanpelle evanpelle force-pushed the evan-fetch-cosmetics branch from 22c611e to 1bf5079 Compare August 1, 2025 02:05
@evanpelle evanpelle force-pushed the evan-fetch-cosmetics branch from 1bf5079 to 52b8cee Compare August 1, 2025 02:09
@evanpelle evanpelle force-pushed the evan-fetch-cosmetics branch 2 times, most recently from 3387344 to ead47dd Compare August 1, 2025 02:15
@CLAassistant
Copy link

CLAassistant commented Aug 1, 2025

CLA assistant check
All committers have signed the CLA.

@evanpelle evanpelle changed the base branch from v24 to main August 1, 2025 02:15
@evanpelle evanpelle modified the milestones: v24, v25 Aug 1, 2025
coderabbitai[bot]
coderabbitai bot previously approved these changes Aug 1, 2025
@evanpelle evanpelle marked this pull request as ready for review August 1, 2025 02:27
@evanpelle evanpelle requested a review from scottanderson August 1, 2025 02:27
Copy link
Contributor

@scottanderson scottanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only had time to review about half of this one right now. I'll take another look at this later.

coderabbitai[bot]
coderabbitai bot previously approved these changes Aug 2, 2025
Copy link
Contributor

@scottanderson scottanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make the necessary changes to cosmetics.json as a static file, before moving it out to the API? This PR does more than just fetch the file.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
src/client/Cosmetics.ts (2)

12-24: Address data mutation and iteration performance issues

The code has two issues that were flagged in previous reviews:

  1. Data mutation: Line 16 directly mutates patternData.product, which affects the shared cosmetics data for other callers
  2. Unnecessary garbage generation: Using Object.entries() creates temporary arrays when direct iteration would be more efficient

Apply this diff to fix both issues:

-  for (const [name, patternData] of Object.entries(cosmetics.patterns)) {
+  for (const name in cosmetics.patterns) {
+    const patternData = cosmetics.patterns[name];
     const hasAccess = playerFlares.has(`pattern:${name}`);
     if (hasAccess) {
       // Remove product info because player already has access.
-      patternData.product = null;
-      patterns.push(patternData);
+      patterns.push({ ...patternData, product: null });
     } else if (patternData.product !== null) {
       // Player doesn't have access, but product is available for purchase.
       patterns.push(patternData);

63-80: Error handling approach needs adjustment

The function currently swallows all errors and returns a fallback object. As noted in previous reviews, this prevents callers from knowing when something went wrong.

Apply this diff to let callers handle errors appropriately:

-async function getCosmetics(): Promise<Cosmetics> {
+async function getCosmetics(): Promise<Cosmetics | undefined> {
   try {
     const response = await fetch(`${getApiBase()}/cosmetics.json`);
     if (!response.ok) {
-      throw new Error(`HTTP error! status: ${response.status}`);
+      console.error(`HTTP error! status: ${response.status}`);
+      return;
     }
     const result = CosmeticsSchema.safeParse(await response.json());
     if (!result.success) {
-      throw new Error(`Invalid cosmetics: ${result.error.message}`);
+      console.error(`Invalid cosmetics: ${result.error.message}`);
+      return;
     }
     return result.data;
   } catch (error) {
     console.error("Error getting cosmetics:", error);
-    return {
-      patterns: {},
-    };
+    return;
   }
 }

You'll also need to update the patterns function to handle the undefined case:

 export async function patterns(
   userMe: UserMeResponse | null,
 ): Promise<Pattern[]> {
   const cosmetics = await getCosmetics();
+  if (!cosmetics) {
+    return [];
+  }
   const patterns: Pattern[] = [];
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3ec9ad and 1769338.

📒 Files selected for processing (13)
  • .gitignore (1 hunks)
  • resources/cosmetics/roles.txt (0 hunks)
  • src/client/Cosmetics.ts (1 hunks)
  • src/client/TerritoryPatternsModal.ts (6 hunks)
  • src/client/graphics/layers/ChatModal.ts (1 hunks)
  • src/client/graphics/layers/EmojiTable.ts (2 hunks)
  • src/client/graphics/layers/PlayerPanel.ts (1 hunks)
  • src/core/CosmeticSchemas.ts (1 hunks)
  • src/core/CustomFlag.ts (4 hunks)
  • src/server/Privilege.ts (6 hunks)
  • src/server/PrivilegeRefresher.ts (1 hunks)
  • src/server/Worker.ts (5 hunks)
  • tests/server/Privilege.customFlag.test.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • resources/cosmetics/roles.txt
🚧 Files skipped from review as they are similar to previous changes (8)
  • .gitignore
  • tests/server/Privilege.customFlag.test.ts
  • src/server/PrivilegeRefresher.ts
  • src/core/CustomFlag.ts
  • src/server/Worker.ts
  • src/core/CosmeticSchemas.ts
  • src/client/TerritoryPatternsModal.ts
  • src/server/Privilege.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: andrewNiziolek
PR: openfrontio/OpenFrontIO#1007
File: resources/lang/de.json:115-115
Timestamp: 2025-06-02T14:27:37.609Z
Learning: For OpenFrontIO project: When localization keys are renamed in language JSON files, the maintainers separate technical changes from translation content updates. They wait for community translators to update the actual translation values rather than attempting to translate in the same PR. This allows technical changes to proceed while ensuring accurate translations from native speakers.
Learnt from: scottanderson
PR: openfrontio/OpenFrontIO#786
File: src/server/Client.ts:16-18
Timestamp: 2025-06-12T23:22:55.328Z
Learning: Undefined values for `roles`, `flares`, and `pattern` are intentionally used to mark an unauthenticated client, whereas empty arrays/strings indicate an authenticated user with no roles or flares. Avoid suggesting non-optional types for these fields.
📚 Learning: in src/client/main.ts, during game start in the handlejoinlobby callback, ui elements are hidden usi...
Learnt from: VariableVince
PR: openfrontio/OpenFrontIO#1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Applied to files:

  • src/client/graphics/layers/EmojiTable.ts
  • src/client/graphics/layers/PlayerPanel.ts
  • src/client/graphics/layers/ChatModal.ts
📚 Learning: in the openfrontio codebase, json files should be imported using standard import syntax without impo...
Learnt from: Aotumuri
PR: openfrontio/OpenFrontIO#786
File: src/core/Util.ts:4-4
Timestamp: 2025-06-07T13:15:55.439Z
Learning: In the OpenFrontIO codebase, JSON files should be imported using standard import syntax without import attributes, as the TypeScript configuration supports resolveJsonModule and the codebase already uses this pattern successfully in files like src/client/Cosmetic.ts.

Applied to files:

  • src/client/Cosmetics.ts
🪛 Biome (2.1.2)
src/client/graphics/layers/EmojiTable.ts

[error] 56-56: Expected a statement but instead found ')'.

Expected a statement here.

(parse)


[error] 56-58: Expected a statement but instead found '=======

ead47dd (remote cosmetics)'.

Expected a statement here.

(parse)

src/client/graphics/layers/ChatModal.ts

[error] 188-190: Expected a statement but instead found '=======
initEventBus()'.

Expected a statement here.

(parse)


[error] 191-192: Expected a statement but instead found '>>>>>>> 07e275a (Allow additional modals to close when clicking the Escape key (#1604))'.

Expected a statement here.

(parse)


[error] 192-192: numbers cannot be followed by identifiers directly after

an identifier cannot appear here

(parse)

🪛 GitHub Check: 🔍 ESLint
src/client/graphics/layers/EmojiTable.ts

[failure] 51-51:
Parsing error: Merge conflict marker encountered.

src/client/graphics/layers/ChatModal.ts

[failure] 185-185:
Parsing error: Merge conflict marker encountered.

🪛 GitHub Actions: 🧪 CI
src/client/graphics/layers/EmojiTable.ts

[error] 51-51: TS1185: Merge conflict marker encountered.


[error] 57-57: TS1185: Merge conflict marker encountered.


[error] 58-58: TS1185: Merge conflict marker encountered.


[error] 52-52: TS2552: Cannot find name 'CloseViewEvent'. Did you mean 'CloseEvent'?

src/client/graphics/layers/ChatModal.ts

[error] 185-185: TS1185: Merge conflict marker encountered.


[error] 189-189: TS1185: Merge conflict marker encountered.


[error] 192-192: TS1185: Merge conflict marker encountered.


[error] 176-176: TS2393: Duplicate function implementation.


[error] 186-186: TS2393: Duplicate function implementation.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Deploy to openfront.dev
🔇 Additional comments (2)
src/client/Cosmetics.ts (2)

8-10: Clean refactor to dynamic cosmetics fetching

The switch from static imports to dynamic API fetching aligns well with the PR objectives. The flare-based access control is simpler and more maintainable than the previous role system.


27-61: Purchase handling looks clean and robust

The error handling with specific 401 response handling and user-friendly alerts is well implemented. The function structure is clear and follows good TypeScript practices.

Comment on lines 51 to 58
<<<<<<< HEAD
eventBus.on(CloseViewEvent, (e) => {
if (!this.hidden) {
this.hideTable();
}
});
=======
>>>>>>> ead47dd1 (remote cosmetics)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix merge conflict markers.

The file has unresolved merge conflict markers that prevent compilation. Based on the changes removing CloseViewEvent handling, the conflicted code should be removed entirely.

Apply this diff to resolve the merge conflict:

-<<<<<<< HEAD
-
-    eventBus.on(CloseViewEvent, (e) => {
-      if (!this.hidden) {
-        this.hideTable();
-      }
-    });
-=======
->>>>>>> ead47dd1 (remote cosmetics)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<<<<<<< HEAD
eventBus.on(CloseViewEvent, (e) => {
if (!this.hidden) {
this.hideTable();
}
});
=======
>>>>>>> ead47dd1 (remote cosmetics)
🧰 Tools
🪛 Biome (2.1.2)

[error] 56-56: Expected a statement but instead found ')'.

Expected a statement here.

(parse)


[error] 56-58: Expected a statement but instead found '=======

ead47dd (remote cosmetics)'.

Expected a statement here.

(parse)

🪛 GitHub Check: 🔍 ESLint

[failure] 51-51:
Parsing error: Merge conflict marker encountered.

🪛 GitHub Actions: 🧪 CI

[error] 51-51: TS1185: Merge conflict marker encountered.


[error] 57-57: TS1185: Merge conflict marker encountered.


[error] 58-58: TS1185: Merge conflict marker encountered.


[error] 52-52: TS2552: Cannot find name 'CloseViewEvent'. Did you mean 'CloseEvent'?

🤖 Prompt for AI Agents
In src/client/graphics/layers/EmojiTable.ts between lines 51 and 58, there are
unresolved merge conflict markers around the CloseViewEvent event handler.
Remove the entire block containing the eventBus.on(CloseViewEvent, ...) listener
along with the conflict markers to resolve the conflict and ensure the file
compiles correctly.

Comment on lines 182 to 184
this.eventBus.on(CloseViewEvent, (e) => {
this.hide();
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove duplicate event listener.

There's already a CloseViewEvent listener at lines 178-180 that does the same thing. This duplicate listener is unnecessary and could cause hide() to be called multiple times for the same event.

Apply this diff to remove the duplicate listener:

-    this.eventBus.on(CloseViewEvent, (e) => {
-      this.hide();
-    });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.eventBus.on(CloseViewEvent, (e) => {
this.hide();
});
// (Lines 178–180: original CloseViewEvent listener)
this.eventBus.on(CloseViewEvent, (e) => {
this.hide();
});
// Duplicate listener at lines 182–184 has been removed
🤖 Prompt for AI Agents
In src/client/graphics/layers/PlayerPanel.ts around lines 182 to 184, there is a
duplicate event listener for CloseViewEvent that calls this.hide(), which is
already handled by a listener at lines 178 to 180. Remove the listener at lines
182 to 184 to avoid calling hide() multiple times for the same event.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
tests/server/Privilege.customFlag.test.ts (3)

5-8: Replace throw-away stub with an explicit Jest mock

Using an inline function that always throws hides why the decoder is called if it ever is.
A Jest mock makes intent clearer and gives you an assertion point:

const dummyPatternDecoder = jest.fn(() => {
  throw new Error("Pattern decoding should not be reached in flag tests");
});

30-76: Collapse repetitive cases with it.each

Many assertions differ only by input tuple. Parameterising them cuts noise and documents the matrix succinctly:

it.each([
  ["!b-b", [], true],
  ["!a-b", ["cosmetic:flags"], true],
  // …
])("flag %s with flares %j → %s", (flag, flares, expected) => {
  expect(checker.isCustomFlagAllowed(flag, flares)).toBe(expected);
});

9-26: Optional: give every mock layer/color an explicit flares array

Leaving flares undefined relies on implicit “unrestricted” semantics.
Being explicit (flares: []) makes the intent unambiguous and guards against future schema changes that might treat undefined differently from an empty list.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69d20dc and 0caabd2.

📒 Files selected for processing (10)
  • .gitignore (1 hunks)
  • resources/cosmetics/roles.txt (0 hunks)
  • src/client/Cosmetics.ts (1 hunks)
  • src/client/TerritoryPatternsModal.ts (6 hunks)
  • src/core/CosmeticSchemas.ts (1 hunks)
  • src/core/CustomFlag.ts (4 hunks)
  • src/server/Privilege.ts (6 hunks)
  • src/server/PrivilegeRefresher.ts (1 hunks)
  • src/server/Worker.ts (5 hunks)
  • tests/server/Privilege.customFlag.test.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • resources/cosmetics/roles.txt
🚧 Files skipped from review as they are similar to previous changes (8)
  • .gitignore
  • src/core/CustomFlag.ts
  • src/core/CosmeticSchemas.ts
  • src/server/PrivilegeRefresher.ts
  • src/server/Worker.ts
  • src/server/Privilege.ts
  • src/client/Cosmetics.ts
  • src/client/TerritoryPatternsModal.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: andrewNiziolek
PR: openfrontio/OpenFrontIO#1007
File: resources/lang/de.json:115-115
Timestamp: 2025-06-02T14:27:37.609Z
Learning: For OpenFrontIO project: When localization keys are renamed in language JSON files, the maintainers separate technical changes from translation content updates. They wait for community translators to update the actual translation values rather than attempting to translate in the same PR. This allows technical changes to proceed while ensuring accurate translations from native speakers.
Learnt from: scottanderson
PR: openfrontio/OpenFrontIO#786
File: src/server/Client.ts:16-18
Timestamp: 2025-06-12T23:22:55.328Z
Learning: Undefined values for `roles`, `flares`, and `pattern` are intentionally used to mark an unauthenticated client, whereas empty arrays/strings indicate an authenticated user with no roles or flares. Avoid suggesting non-optional types for these fields.
📚 Learning: in the openfrontio codebase, `checkpermission()` function's return values need to be handled with de...
Learnt from: Aotumuri
PR: openfrontio/OpenFrontIO#709
File: src/client/Main.ts:276-296
Timestamp: 2025-05-16T12:06:01.732Z
Learning: In the OpenFrontIO codebase, `checkPermission()` function's return values need to be handled with defensive checks using `Array.isArray()`, even though its TypeScript signature indicates it returns arrays. Removing these checks breaks functionality.

Applied to files:

  • tests/server/Privilege.customFlag.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy to openfront.dev
🔇 Additional comments (1)
tests/server/Privilege.customFlag.test.ts (1)

2-2: Confirm correct export name

Double-check that PrivilegeCheckerImpl is exported as a named export from src/server/Privilege.ts.
If the implementation switched to a default export during the refactor, this import will break the tests.

};

const checker = new PrivilegeChecker(mockCosmetics, dummyPatternDecoder);
const checker = new PrivilegeCheckerImpl(mockCosmetics, dummyPatternDecoder);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Instantiate checker per test to avoid shared state

PrivilegeCheckerImpl might cache results internally.
Creating a single instance for the entire suite risks cross-test leakage.
Wrap construction in a beforeEach:

let checker: PrivilegeCheckerImpl;
beforeEach(() => {
  checker = new PrivilegeCheckerImpl(mockCosmetics, dummyPatternDecoder);
});
🤖 Prompt for AI Agents
In tests/server/Privilege.customFlag.test.ts at line 28, the
PrivilegeCheckerImpl instance is created once and shared across tests, risking
state leakage. To fix this, remove the direct instantiation at line 28 and
instead declare a checker variable outside tests, then instantiate it inside a
beforeEach block so each test gets a fresh instance.

@evanpelle evanpelle changed the title fetch cosmetics.json Remove role based perms, fetch cosmetics.json from api Aug 4, 2025
@evanpelle
Copy link
Collaborator Author

Can we make the necessary changes to cosmetics.json as a static file, before moving it out to the API? This PR does more than just fetch the file.

Ack, it's a lot of work to split up.

@evanpelle evanpelle force-pushed the evan-fetch-cosmetics branch from 0caabd2 to 423b602 Compare August 4, 2025 23:41
@evanpelle evanpelle merged commit 00668dd into main Aug 4, 2025
10 of 11 checks passed
@evanpelle evanpelle deleted the evan-fetch-cosmetics branch August 4, 2025 23:48
@github-project-automation github-project-automation bot moved this from Development to Complete in OpenFront Release Management Aug 4, 2025
Lavodan pushed a commit to Lavodan/OpenFrontIO_changes that referenced this pull request Dec 2, 2025
)

## Description:

* Fetch cosmetics.json from api
* Remove all role based perms, we are only using flares now
* Created Priviledge refresher which periodically polls /cosmetics.json
endpoint.

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA agreement (only required once).

## Please put your Discord username so you can be contacted if a bug or
regression is found:

evan
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Complete

Development

Successfully merging this pull request may close these issues.

3 participants