Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"write-heading-ids": "docusaurus write-heading-ids",
"docker:build": "docker build . -t openfga-docs-ui",
"docker:run": "docker run -p ${PORT:-3000}:3000 openfga-docs-ui",
"typecheck": "tsc --skipLibCheck",
"lint": "eslint . --ext .ts --ext .tsx",
"typecheck": "tsc",
"lint": "eslint .",
"lint:fix": "npm run lint -- --fix",
"format:check": "prettier --check src/**",
"format:fix": "prettier --write src/**"
Expand Down Expand Up @@ -61,7 +61,7 @@
"husky": "9.1.7",
"markdown-link-check": "^3.14.2",
"prettier": "3.8.4",
"typescript": "5.9.3"
"typescript": "6.0.3"
},
"overrides": {
"semver": "^7.7.1"
Expand Down
9 changes: 6 additions & 3 deletions src/components/Docs/AuthorizationModel/SyntaxTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,21 @@ export enum SyntaxFormat {
Dsl = 'dsl',
}

const isTypeDefinition = (config: AuthorizationModel | TypeDefinition): config is TypeDefinition =>
!(config as AuthorizationModel).type_definitions && Boolean((config as TypeDefinition).type);

export const loadSyntax = (
configuration: AuthorizationModel,
format: SyntaxFormat = SyntaxFormat.Json,
skipVersion?: boolean,
) => {
let config = configuration;
let config: AuthorizationModel = configuration;
switch (format) {
case SyntaxFormat.Dsl:
if (!config.type_definitions && (config as unknown as TypeDefinition).type) {
if (isTypeDefinition(config)) {
config = {
schema_version: SchemaVersion.OneDotOne,
type_definitions: [config as unknown as TypeDefinition],
type_definitions: [config],
id: '',
};
skipVersion = true;
Expand Down
4 changes: 1 addition & 3 deletions src/components/Docs/RelationshipTuples/Viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ interface RelationshipTuple {

export interface RelationshipCondition {
name: string;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
context?: Record<string, any>;
context?: Record<string, unknown>;
}

interface RelationshipTuplesViewerOpts {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ interface Check {
correlation_id: string;
allowed: boolean;
contextualTuples?: TupleKey[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context?: Record<string, any>;
context?: Record<string, unknown>;
}
interface BatchCheckRequestViewerOpts extends DefaultTabbedViewerOpts {
authorizationModelId?: string;
Expand Down
3 changes: 1 addition & 2 deletions src/components/Docs/SnippetViewer/CheckRequestViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ interface CheckRequestViewerOpts {
allowed: boolean;
contextualTuples?: TupleKey[];

// eslint-disable-next-line @typescript-eslint/no-explicit-any
context?: Record<string, any>;
context?: Record<string, unknown>;
skipSetup?: boolean;
pseudoCodeMode?: boolean;
allowedLanguages?: SupportedLanguage[];
Expand Down
23 changes: 9 additions & 14 deletions src/components/Docs/SnippetViewer/ExecuteApiRequestViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import assertNever from 'assert-never/index';
// Helpers – value serialisation per language
// ---------------------------------------------------------------------------

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function jsValue(val: any, indent: number): string {
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };

function jsValue(val: JsonValue, indent: number): string {
Comment on lines +9 to +11

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Serializer coverage is incomplete for declared JsonValue inputs.

JsonValue includes null and arrays, but pyValue/goValue/javaValue/csharpValue don’t consistently emit valid literals for those cases, so some generated snippets become invalid or semantically wrong.

💡 Suggested fix
 function pyValue(val: JsonValue, indent: number): string {
+  if (val === null) return 'None';
   if (typeof val === 'string') return `"${val}"`;
   if (typeof val === 'number') return String(val);
   if (typeof val === 'boolean') return val ? 'True' : 'False';
@@
 function goValue(val: JsonValue, indent: number): string {
+  if (val === null) return 'nil';
   if (typeof val === 'string') return `"${val}"`;
   if (typeof val === 'number' || typeof val === 'boolean') return String(val);
+  if (Array.isArray(val)) {
+    const items = val.map((v) => goValue(v, indent + 4));
+    return `[]interface{}{${items.join(', ')}}`;
+  }
   if (typeof val === 'object' && val !== null) {
@@
 function javaValue(val: JsonValue, indent: number): string {
+  if (val === null) return 'null';
   if (typeof val === 'string') return `"${val}"`;
   if (typeof val === 'number' || typeof val === 'boolean') return String(val);
+  if (Array.isArray(val)) {
+    const items = val.map((v) => javaValue(v, indent + 4));
+    return `List.of(${items.join(', ')})`;
+  }
   if (typeof val === 'object' && val !== null) {
@@
 function csharpValue(val: JsonValue, indent: number): string {
+  if (val === null) return 'null';
   if (typeof val === 'string') return `"${val}"`;
   if (typeof val === 'number' || typeof val === 'boolean') return String(val).toLowerCase();
+  if (Array.isArray(val)) {
+    const items = val.map((v) => csharpValue(v, indent + 4));
+    return `new[] { ${items.join(', ')} }`;
+  }
   if (typeof val === 'object' && val !== null) {

Also applies to: 27-27, 44-44, 56-56, 68-68

🤖 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 `@src/components/Docs/SnippetViewer/ExecuteApiRequestViewer.tsx` around lines 9
- 11, Serializer handling for JsonValue is incomplete in jsValue, pyValue,
goValue, javaValue, and csharpValue: null and array inputs are not consistently
converted into valid target-language literals. Update each serializer to
explicitly handle null and arrays from the shared JsonValue type, ensuring the
emitted snippets use the correct language-specific representation and remain
syntactically valid for every supported JsonValue case.

if (typeof val === 'string') return `"${val}"`;
if (typeof val === 'number' || typeof val === 'boolean') return String(val);
if (Array.isArray(val)) {
Expand All @@ -23,8 +24,7 @@ function jsValue(val: any, indent: number): string {
return String(val);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function pyValue(val: any, indent: number): string {
function pyValue(val: JsonValue, indent: number): string {
if (typeof val === 'string') return `"${val}"`;
if (typeof val === 'number') return String(val);
if (typeof val === 'boolean') return val ? 'True' : 'False';
Expand All @@ -41,8 +41,7 @@ function pyValue(val: any, indent: number): string {
return String(val);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function goValue(val: any, indent: number): string {
function goValue(val: JsonValue, indent: number): string {
if (typeof val === 'string') return `"${val}"`;
if (typeof val === 'number' || typeof val === 'boolean') return String(val);
if (typeof val === 'object' && val !== null) {
Expand All @@ -54,8 +53,7 @@ function goValue(val: any, indent: number): string {
return String(val);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function javaValue(val: any, indent: number): string {
function javaValue(val: JsonValue, indent: number): string {
if (typeof val === 'string') return `"${val}"`;
if (typeof val === 'number' || typeof val === 'boolean') return String(val);
if (typeof val === 'object' && val !== null) {
Expand All @@ -67,8 +65,7 @@ function javaValue(val: any, indent: number): string {
return String(val);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function csharpValue(val: any, indent: number): string {
function csharpValue(val: JsonValue, indent: number): string {
if (typeof val === 'string') return `"${val}"`;
if (typeof val === 'number' || typeof val === 'boolean') return String(val).toLowerCase();
if (typeof val === 'object' && val !== null) {
Expand Down Expand Up @@ -118,8 +115,7 @@ function buildCurlSnippet(opts: {
path: string;
pathParams?: Record<string, string>;
queryParams?: Record<string, string>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
body?: Record<string, any>;
body?: Record<string, JsonValue>;
streaming?: boolean;
}): string {
const { method, path, pathParams, queryParams, body, streaming } = opts;
Expand Down Expand Up @@ -158,8 +154,7 @@ interface ExecuteApiRequestViewerOpts {
/** Path parameter substitutions. */
pathParams?: Record<string, string>;
/** Request body (omit for GET). */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
body?: Record<string, any>;
body?: Record<string, JsonValue>;
/** Query parameter key-value pairs. */
queryParams?: Record<string, string>;
/** Optional example response shown in a trailing comment. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ interface ListObjectsRequestViewerOpts {
objectType: string;
contextualTuples?: TupleKey[];

// eslint-disable-next-line @typescript-eslint/no-explicit-any
context?: Record<string, any>;
context?: Record<string, unknown>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

context now accepts non-string values, but snippet generation still stringifies them incorrectly.

Several SDK branches quote context values as strings, so booleans/numbers/objects are emitted incorrectly (e.g., "true", "[object Object]"). Align generation with JSON-value-aware serializers, or narrow the type if only strings are supported.

🤖 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 `@src/components/Docs/SnippetViewer/ListObjectsRequestViewer.tsx` at line 13,
Snippet generation is still treating the ListObjectsRequestViewer context values
as strings even though context now allows non-string JSON-like values. Update
the snippet serializer logic in ListObjectsRequestViewer (and any shared snippet
generation helper it uses) to emit booleans, numbers, and objects as proper JSON
values instead of quoted strings, or tighten the context type back to strings if
that is the only supported shape. Use the context prop and any
serializer/formatter methods connected to snippet output as the entry points for
the fix.

expectedResults: string[];
skipSetup?: boolean;
pseudoCodeMode?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ interface ListUsersRequestViewerOpts {
userFilterRelation?: string;
contextualTuples?: TupleKey[];

// eslint-disable-next-line @typescript-eslint/no-explicit-any
context?: Record<string, any>;
context?: Record<string, unknown>;
expectedResults: ListUsersResponse;
skipSetup?: boolean;
pseudoCodeMode?: boolean;
Expand Down
13 changes: 8 additions & 5 deletions src/components/icons/animations/AnimatedIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import * as React from 'react';
import { lazy, Suspense } from 'react';
Comment on lines 1 to +2
import BrowserOnly from '@docusaurus/BrowserOnly';

const Player = lazy(() => import('@lottiefiles/react-lottie-player').then((module) => ({ default: module.Player })));

type AnimatedIconProps = { element?: string | object };
const AnimatedIcon: React.FC<AnimatedIconProps> = ({ element }) => {
return (
<div style={{ height: '100%' }}>
<BrowserOnly>
{() => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { Player } = require('@lottiefiles/react-lottie-player');
return <Player autoplay={true} loop={true} controls={true} src={element} />;
}}
{() => (
<Suspense fallback={null}>
<Player autoplay={true} loop={true} controls={true} src={element} />
</Suspense>
)}
</BrowserOnly>
</div>
);
Expand Down
5 changes: 3 additions & 2 deletions src/components/icons/animations/auth0.lottie.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const auth0Lottie: any = {
import type { LottieAnimationData } from './types';

export const auth0Lottie: LottieAnimationData = {
v: '5.7.4',
fr: 60,
ip: 0,
Expand Down
5 changes: 3 additions & 2 deletions src/components/icons/animations/code.lottie.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const codeLottie: any = {
import type { LottieAnimationData } from './types';

export const codeLottie: LottieAnimationData = {
v: '5.7.4',
fr: 60,
ip: 0,
Expand Down
5 changes: 3 additions & 2 deletions src/components/icons/animations/community.lottie.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const communityLottie: any = {
import type { LottieAnimationData } from './types';

export const communityLottie: LottieAnimationData = {
v: '5.7.4',
fr: 60,
ip: 0,
Expand Down
5 changes: 3 additions & 2 deletions src/components/icons/animations/fast.lottie.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const fastLottie: any = {
import type { LottieAnimationData } from './types';

export const fastLottie: LottieAnimationData = {
v: '5.7.4',
fr: 60,
ip: 0,
Expand Down
5 changes: 3 additions & 2 deletions src/components/icons/animations/model.lottie.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const modelLottie: any = {
import type { LottieAnimationData } from './types';

export const modelLottie: LottieAnimationData = {
v: '5.7.4',
fr: 60,
ip: 0,
Expand Down
5 changes: 3 additions & 2 deletions src/components/icons/animations/open.lottie.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const openLottie: any = {
import type { LottieAnimationData } from './types';

export const openLottie: LottieAnimationData = {
v: '5.7.4',
fr: 60,
ip: 0,
Expand Down
1 change: 1 addition & 0 deletions src/components/icons/animations/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type LottieAnimationData = Record<string, unknown>;
15 changes: 10 additions & 5 deletions src/pages/api/service.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import * as React from 'react';
import { lazy, Suspense } from 'react';
Comment on lines 1 to +2
import Layout from '@theme/Layout';
import BrowserOnly from '@docusaurus/BrowserOnly';
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';

const SwaggerUI = lazy(() =>
import('../../components/SwaggerUI/swagger-ui').then((module) => ({ default: module.SwaggerUI })),
);

const ApiService = () => {
const { siteConfig } = useDocusaurusContext();
const apiDocsBasePath = siteConfig.customFields?.apiDocsBasePath as string | undefined;
Expand All @@ -16,11 +21,11 @@ const ApiService = () => {
return (
<Layout title="Open FGA API Explorer">
<BrowserOnly fallback={<div>Loading...</div>}>
{() => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { SwaggerUI } = require('../../components/SwaggerUI/swagger-ui');
return <SwaggerUI apiDocsBasePath={apiDocsBasePath} />;
}}
{() => (
<Suspense fallback={<div>Loading...</div>}>
<SwaggerUI apiDocsBasePath={apiDocsBasePath} />
</Suspense>
)}
</BrowserOnly>
</Layout>
);
Expand Down
13 changes: 10 additions & 3 deletions src/theme/NavbarItem/NavbarNavLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ interface GithubStarsSessionStorage {
retrievedTime: number;
}

function readCachedGithubStars(): GithubStarsSessionStorage | null {
const raw = sessionStorage.getItem(GITHUB_STARS_SESSION_STORAGE_NAME);
if (!raw) {
return null;
}
const parsed = JSON.parse(raw);
return typeof parsed?.count === 'number' && typeof parsed?.retrievedTime === 'number' ? parsed : null;
}
Comment on lines +16 to +23
Comment on lines +16 to +23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard cache parsing/storage access so errors don’t escape getData().

readCachedGithubStars() can throw (invalid JSON or storage access), and it’s called before the try block in getData(). That bypasses your fallback path and can break the effect flow.

🛡️ Proposed fix
 function readCachedGithubStars(): GithubStarsSessionStorage | null {
-  const raw = sessionStorage.getItem(GITHUB_STARS_SESSION_STORAGE_NAME);
-  if (!raw) {
+  try {
+    const raw = sessionStorage.getItem(GITHUB_STARS_SESSION_STORAGE_NAME);
+    if (!raw) {
+      return null;
+    }
+    const parsed: unknown = JSON.parse(raw);
+    if (
+      typeof parsed === 'object' &&
+      parsed !== null &&
+      typeof (parsed as { count?: unknown }).count === 'number' &&
+      typeof (parsed as { retrievedTime?: unknown }).retrievedTime === 'number'
+    ) {
+      return parsed as GithubStarsSessionStorage;
+    }
     return null;
+  } catch {
+    return null;
   }
-  const parsed = JSON.parse(raw);
-  return typeof parsed?.count === 'number' && typeof parsed?.retrievedTime === 'number' ? parsed : null;
 }

Also applies to: 47-47

🤖 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 `@src/theme/NavbarItem/NavbarNavLink.tsx` around lines 16 - 23,
`readCachedGithubStars()` can throw before `getData()` reaches its existing
fallback handling, so wrap the sessionStorage read and JSON.parse inside the
same guarded flow used by `getData()`. Update `readCachedGithubStars` (or move
its call into `getData`) so storage access and cache parsing failures are caught
and return null, preserving the effect’s fallback path and preventing uncaught
errors from escaping.


export default function NavbarNavLink({
activeBasePath,
activeBaseRegex,
Expand All @@ -35,9 +44,7 @@ export default function NavbarNavLink({

useEffect(() => {
const getData = async () => {
const cachedGithubStars = JSON.parse(
sessionStorage.getItem(GITHUB_STARS_SESSION_STORAGE_NAME),
) as unknown as GithubStarsSessionStorage;
const cachedGithubStars = readCachedGithubStars();
try {
if (cachedGithubStars) {
const cacheExpiryTime = new Date(cachedGithubStars.retrievedTime);
Expand Down
6 changes: 3 additions & 3 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"@features/*": ["src/features/*"],
"@static/*": ["static/*"]
},
"module": "Node16",
"moduleResolution": "Node16",
"jsx": "react"
"jsx": "react-jsx",
"strict": false,
"ignoreDeprecations": "6.0"
}
}
Loading